Last active
August 11, 2021 11:57
-
-
Save hieptl/157445b56fc2d6a9e69e6c5203f1b107 to your computer and use it in GitHub Desktop.
Input Box Component - Facebook Clone
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// import useRef, useState and useContext. | |
import { useRef, useState, useContext } from "react"; | |
// import Context to get shared data from react context. | |
import Context from '../Context'; | |
// import real time data and storage to interact with real time database and upload files to Firebase. | |
import { realTimeDb, storage } from "../firebase"; | |
// import uuid to generate id for posts. | |
import { v4 as uuidv4 } from "uuid"; | |
function InputBox() { | |
// uploaded image state. | |
const [imageToPost, setImageToPost] = useState(null); | |
// input ref and file ref to get input value and uploaded file. | |
const inputRef = useRef(null); | |
const filepickerRef = useRef(null); | |
// get shared data from React context. | |
const { user, setIsLoading, wallPosts, setWallPosts } = useContext(Context); | |
const updateWallPosts = (post) => { | |
if (post) { | |
const updatedwallPosts = [...wallPosts, post]; | |
setWallPosts(updatedwallPosts.sort((a,b) => { | |
return new Date(b.timestamp) - new Date(a.timestamp); | |
})); | |
} | |
} | |
/** | |
* send post to Firebase | |
* @param {*} e | |
* @returns | |
*/ | |
const sendPost = (e) => { | |
e.preventDefault(); | |
if (!inputRef.current.value) return; | |
// show loading indicator. | |
setIsLoading(true); | |
// generate uuid for the post. | |
const postUuid = uuidv4(); | |
// create request payload. | |
const post = { | |
id: postUuid, | |
message: inputRef.current.value, | |
timestamp: new Date().toString(), | |
createdBy: user.email.substring(0, user.email.indexOf('@')), | |
userAvatar: user.avatar | |
} | |
// insert post to real time database - Firebase. | |
realTimeDb.ref(`posts/${postUuid}`).set(post).then(() => { | |
if (imageToPost) { | |
// if the end-user has uploaded image for the post. The image will be uploaded to firebase storage. | |
setIsLoading(true); | |
// upload image to Firebase storage. | |
const uploadTask = storage.ref(`posts/${postUuid}`).putString(imageToPost, "data_url"); | |
// remove selected image from UI. | |
removeImage(); | |
// keep track the uploading progress. | |
uploadTask.on("state_changed", null, (error) => { | |
alert(error); | |
// hide loading indicator. | |
setIsLoading(false); | |
}, () => { | |
storage.ref("posts").child(postUuid).getDownloadURL().then((url) => { | |
// update post with the uploaded url. | |
post.imageUrl = url; | |
// set wall posts | |
updateWallPosts(post); | |
// update the post to real time database. | |
realTimeDb.ref(`posts/${postUuid}`).set(post); | |
// hide loading indicator. | |
setIsLoading(false); | |
}); | |
} | |
); | |
} else { | |
// set wall posts | |
updateWallPosts(post); | |
} | |
// hide loading indicator. | |
setIsLoading(false); | |
}); | |
// reset user's input value. | |
inputRef.current.value = ""; | |
}; | |
/** | |
* get the uploaded image. | |
* @param {*} e | |
*/ | |
const addImageToPost = (e) => { | |
const reader = new FileReader(); | |
if (e.target.files[0]) { | |
reader.readAsDataURL(e.target.files[0]); | |
} | |
reader.onload = (readerEvent) => { | |
setImageToPost(readerEvent.target.result); | |
}; | |
}; | |
/** | |
* remove image from the UI. | |
*/ | |
const removeImage = () => { | |
filepickerRef.current.value = null; | |
setImageToPost(null); | |
}; | |
return ( | |
<div className="inputbox__container"> | |
<div className="inputbox__wrapper"> | |
<img className="inputbox__useravatar" src={user.avatar} /> | |
<form className="inputbox__inputcontainer"> | |
<input | |
className="inputbox__input" | |
type="text" | |
placeholder={`What's on your mind ${""}?`} | |
ref={inputRef} | |
/> | |
<button hidden onClick={sendPost}> | |
Submit | |
</button> | |
</form> | |
</div> | |
{imageToPost && ( | |
<div onClick={removeImage} className="inputbox__imgcontainer"> | |
<img className="inputbox__img" src={imageToPost} alt="" /> | |
<svg className="inputbox__imgremove" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="24" height="24" fill="currentColor"> | |
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" /> | |
</svg> | |
</div> | |
)} | |
<div className="inputbox__footer"> | |
<div className="inputIcon inputbox__footer-item"> | |
<i | |
data-visualcompletion="css-img" | |
className="hu5pjgll lzf7d6o1" | |
style={{ | |
backgroundImage: | |
'url("https://static.xx.fbcdn.net/rsrc.php/v3/yQ/r/7ml5sBBNykf.png")', | |
backgroundPosition: "0px 0px", | |
backgroundSize: "auto", | |
width: "24px", | |
height: "24px", | |
backgroundRepeat: "no-repeat", | |
display: "inline-block", | |
}} | |
></i> | |
<p className="inputbox__footer-item-title">Live Video</p> | |
</div> | |
<div | |
onClick={() => filepickerRef.current.click()} | |
className="inputIcon inputbox__footer-item" | |
> | |
<i | |
data-visualcompletion="css-img" | |
className="hu5pjgll lzf7d6o1" | |
style={{ | |
backgroundImage: | |
'url("https://static.xx.fbcdn.net/rsrc.php/v3/yQ/r/7ml5sBBNykf.png")', | |
backgroundPosition: "0px -175px", | |
backgroundSize: "auto", | |
width: "24px", | |
height: "24px", | |
backgroundRepeat: "no-repeat", | |
display: "inline-block", | |
}} | |
></i> | |
<p className="inputbox__footer-item-title">Photo/Video</p> | |
<input | |
onChange={addImageToPost} | |
ref={filepickerRef} | |
type="file" | |
hidden | |
/> | |
</div> | |
<div className="inputIcon inputbox__footer-item"> | |
<i | |
data-visualcompletion="css-img" | |
className="hu5pjgll lzf7d6o1" | |
style={{ | |
backgroundImage: | |
'url("https://static.xx.fbcdn.net/rsrc.php/v3/yQ/r/7ml5sBBNykf.png")', | |
backgroundPosition: "0px -25px", | |
backgroundSize: "auto", | |
width: "24px", | |
height: "24px", | |
backgroundRepeat: "no-repeat", | |
display: "inline-block", | |
}} | |
></i> | |
<p className="inputbox__footer-item-title">Feeling/Activity</p> | |
</div> | |
</div> | |
</div> | |
); | |
} | |
export default InputBox; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment