I'm working with Firebase and i'm doing a fileuploader - javascript

Whenever i try to upload an image, i get my own created error that it's not the right file type. I tried adding different file types and adding more acceptable file types.
import React, { Fragment, useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlus } from "#fortawesome/free-solid-svg-icons";
import "./FileUpload.css";
import ProgressBar from "./ProgressBar";
const FileUpload = () => {
const [file, setFile] = useState(null);
const [error, setError] = useState(null);
const types = ["image/png, image/gif, image/jpeg"];
const uploadHandler = (e) => {
const selected = e.target.files[0];
console.log(selected);
if (selected && types.includes(selected.type)) {
setFile(selected);
setError(" ");
} else {
setFile(null);
setError("Please pick an image file that is a png or jpg");
}
};
return (
<Fragment>
<div className="file-card">
<div className="file-inputs">
<input type="file" className="input-files" onChange={uploadHandler} />
<button className="button-files">
<i className="button-i">
<FontAwesomeIcon icon={faPlus} />
</i>
</button>
</div>
<p className="main">Supported Files</p>
<p className="info">PDF, JPG,PNG</p>
</div>
<div className="output">
{error && <div className="error">{error}</div>}
{file && <div>{file.name}</div>}
{file && <ProgressBar file={file} setFile={setFile} />}
</div>
</Fragment>
);
};
export default FileUpload;
This is what shows when i try to upload a file. It's the right file type. it just doesn't upload. I tried adding different acceptable ones and still get the same issue.
I created this hook for storage, in order to decrease the code i have in my file uploader. The hook should be in charge of adding the files to firebase.
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { projectStorage } from "../../firebase";
import { useEffect, useState } from "react";
const useStorage = (file) => {
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
const [url, setUrl] = useState(null);
useEffect(() => {
const storageRef = ref(projectStorage, "images/" + file.name);
console.log(storageRef);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
let percentage =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
setProgress(percentage);
},
(err) => {
setError(err);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((url) => setUrl(url));
}
);
}, [file]);
return { progress, url, error };
};
export default useStorage;

Related

Firestorage download urls not working as img sources

I'm using getDownloadURL from fire storage to get a url for any image that is uploaded on to the server. But when I try to display the images in my img element source, it returns a blank icon instead of the actual image. The image even on firestorage has trouble rendering and when its opened, its just a white square instead of the image. The function is the sendImage function in the code. Ive also added CORS configuration to my google cloud shell and changed the meta data multiple times, and yes the images do have tokens and the rules are fine. Any help .
import React, { useContext, useEffect, useRef, useState } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faPaperclip, faImage, faPaperPlane } from '#fortawesome/free-solid-svg-icons'
import { doc, db } from '../firebase'
import { arrayUnion, onSnapshot, setDoc, updateDoc } from 'firebase/firestore'
import ChatContext from '../Context/ChatContext'
import { AuthContext } from '../Context/AuthContext'
import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
const Chat = () => {
const {user} = useContext(AuthContext)
const [messages, setMessages] = useState([])
const {data} = useContext(ChatContext)
const [file, setFile] = useState()
const [msg, setMsg] = useState()
const aref = useRef(null)
const scrollRef = useRef(null)
const photo = '013bfa05b2955d0bff2823c2f3304e1838ac30801c.jpg'
const storage = getStorage()
const storageRef = ref(storage, `images/${photo}`)
const metadata = {
contentDisposition: 'inline',
contentType: 'image/jpeg'
};
useEffect(() => {
const unSub = onSnapshot(doc(db, "messages", data.chatId), (doc) => {
doc.exists() && setMessages(doc.data().messages);
});
return () => {
unSub();
};
}, [data.chatId]);
useEffect(()=>{
scrollToBottom()
}, [messages])
const sendImage = async() =>{
const uploadTask = uploadBytesResumable(storageRef, file, metadata);
await uploadTask.on(
(error) => {
console.log(error)
},
async() => {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
await getDownloadURL(uploadTask.snapshot.ref).then(async(downloadURL) => {
await updateDoc(doc(db, 'messages', data.chatId), {
messages: arrayUnion({photoURL: downloadURL})
})
console.log(downloadURL)
});
}
);
}
const handleChange = (e) =>{
setMsg({msg: e.target.value, origin: user.uid})
}
const sendMessage = async() =>{
if(data.chatId!='global'){
if(msg){
await updateDoc(doc(db, 'messages', data.chatId), {
messages: arrayUnion(msg)
})
}
}
if(file){
sendImage()
}
aref.current.value = ''
}
const handleFileSelect = (e) =>{
e.preventDefault()
setFile({photoURL: e.target.files[0], origin: user.uid})
}
const scrollToBottom = () =>{
scrollRef.current.scrollIntoView({behavior: 'smooth'})
}
const handleKey = (e) => {
e.code === "Enter" && sendMessage();
};
return (
<div className='chatWrapper'>
<button onClick={()=>sendImage()}>here</button>
<div className="cWrapper">
<div className="conversationProfile">
<img className='conversationImage' src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Dwayne_Johnson_2014_%28cropped%29.jpg/640px-Dwayne_Johnson_2014_%28cropped%29.jpg" alt=""/>
<span>DM Name</span>
</div>
<div className="messages">
{messages ? messages?.map((message) =>(
<div className={`message ${message.origin==user.uid && 'sender'}`}>{message.msg ? message.msg : <img className='imgMSG' referrerPolicy='no-referrer' src='https://firebasestorage.googleapis.com/v0/b/mwdims.appspot.com/o/images%2F013bfa05b2955d0bff2823c2f3304e1838ac30801c.jpg?alt=media&token=a95f8bd2-97fe-4bfa-a7d5-36772407c418"
'></img>}</div>
)) : <div className='placeHolder'>Say Something </div>}
<div ref={scrollRef} style={{visibility: 'hidden'}}></div>
</div>
<div className="chatBar">
<input ref={aref} type="text" placeholder='Type Something...' defaultValue='' onChange={handleChange} onKeyDown={handleKey}/>
<div className="chatFunctions">
<FontAwesomeIcon icon={faPaperclip} />
<input type='file' onChange={handleFileSelect}></input>
<FontAwesomeIcon icon={faImage}></FontAwesomeIcon>
<FontAwesomeIcon icon={faPaperPlane} onClick={()=>sendMessage()}/>
</div>
</div>
</div>
</div>
)
}
export default Chat

How to get item.docId so that i can get url for pdf in firestore

i'm trying to implement pdf viewer from url stored in firestore in react js
how i can get item.docId in setPdfUrls please help me out i'm new to react js and web development
Where I'm stuck is that I don't understand how to do it please help
How to get item.docId so that i can get url for pdf in firestore
`
import React, { useState, useEffect, useContext } from "react";
import { Card, Header, Player } from "../components";
import * as ROUTES from "../constants/routes";
import { FirebaseContext } from "../context/firebase";
import { ref, getDownloadURL } from "firebase/storage";
import { storage } from "../lib/firebase.prod";
import { SelectProfileContainer } from "./profiles";
import { FooterContainer } from "./footer";
export function BrowseContainer({ slides }) {
var [pdfUrls, setPdfUrls] = useState([]);
const [resume, setResume]=useState(null);
useEffect(()=>{
getDownloadURL(ref(storage, 'Resume.pdf')).then((url)=>{
setResume(url);
})
},[]);
const [category, setCategory] = useState("articles");
const [profile, setProfile] = useState({});
const [loading, setLoading] = useState(true);
const [slideRows, setSlideRows] = useState([]);
const { firebase } = useContext(FirebaseContext);
const user = firebase.auth().currentUser || {};
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 3000);
}, [profile.displayName]);
useEffect(() => {
setSlideRows(slides[category]);
}, [slides, category]);
return profile.displayName ? (
<>
<Card.Group>
{slideRows.map((slideItem) => (
<Card key={`${category}-${slideItem.title.toLowerCase()}`}>
<Card.Title>{slideItem.title}</Card.Title>
<Card.Entities>
{slideItem.data.map((item) => (
<Card.Item key={item.docId} item={item}>
<Card.Meta>
<Card.SubTitle>{item.title}</Card.SubTitle>
<br/>
<br/>
</Card.Meta>
<Card.Image
src={item.image} alt={item.title}/>
</Card.Item>
))}
</Card.Entities>
<Card.Feature category={category}>
<Player>
<Player.Button />
<Player.Video src={resume} />
</Player>
</Card.Feature>
</Card>
))}
</Card.Group>
<FooterContainer />
</>
) : (
<SelectProfileContainer user={user} setProfile={setProfile} />
);
}
`

critical dependency the request of a dependency is an expression React.js

I have an issue in my code, for any reason the Webpack is not letting me to import images dynamically:
The used hook "useImage.js":
import React from 'react'
const useImage = (fileName) => {
const [loading, setLoading] = React.useState(true)
const [error, setError] = React.useState(null)
const [image, setImage] = React.useState(null)
React.useEffect(() => {
const fetchImage = async () => {
try {
const response = await import(fileName) // change relative path to suit your needs
setImage(response)
} catch (err) {
setError(err)
} finally {
setLoading(false)
}
}
fetchImage()
}, [fileName])
return {
loading,
error,
image,
}
}
export default useImage;
The current component "Project":
import React from "react";
import "#styles/project.css";
import useImage from "#hooks/useImage"
const Project = ({project}) => {
const {loading, error, image} = useImage("#images/JavaScript Practice/main.png");
return (
<div className="project">
{loading
? <p>Loading</p>
: <img src={image} className="project__img" alt="Image of the product" id="image" />
}
</div>
)
}
export default Project;
The error:
At this point I really do not understand what is wrong, I have tried many things and nothing works, I hope that one of you has an idea of a solution. Thank you very much for your attention.

How can I add my Axio post on submit of my upload form when I am using a custom hook for form validation?

I am trying to allow a user to submit an image file (.jpeg, .png) and a title for that image (text) to my backend API but am having some issues on how to go about it. The reason is because I am using a custom hook named useForm in my upload component so that it can pull the validation functions from the file. But I have a function called postUserImage in my upload component which reads the image file and the title that is inputted by checking the state. How can I use the code that is in postUserImage if my onSubmit is already using the form validation submitHandler? I hope that makes sense, if anyone can help with this it would be great.
Update: Here is a sandbox with code, still haven't figure it out. https://codesandbox.io/s/flamboyant-cray-izcj6z?file=/src/App.js
Upload.jsx:
mport {useEffect, useState} from 'react';
import {} from 'react-router-dom';
import {useDispatch, useSelector} from 'react-redux';
import axios from 'axios';
import {clearMessage} from '../../slices/messages';
import authHeader from '../../services/auth-header';
import useForm from '../../hooks/useForm';
const API_URL = 'http://localhost:8080/api/posts';
function Uploads(onUpload) {
const {user: currentUser} = useSelector((state) => state.auth);
const [file, setFile] = useState();
const [title, setTitle] = useState();
const [description, setDescription] = useState('');
const [loading, setLoading] = useState(false);
const [content, setContent] = useState('');
const [preview, setPreview] = useState(null);
useEffect(() => {}, []);
const onAddImage = (file) => {
window.URL.revokeObjectURL(preview);
if (!file) return;
setPreview(window.URL.createObjectURL(file));
};
const postUserImage = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('file', file);
formData.append('title', title);
const result = await axios.post('/post/upload', formData, {
headers: {...authHeader(), 'Content-Type': 'multipart/form-data'},
});
console.log(result.data);
};
//Custom hook call
// const {handleChange, values, errors, handleSubmit} = useForm(postUserImage);
const initialState = {title: ''};
const validations = [
({title}) => isRequired(title) || {title: 'Title is required'},
];
const {values, isValid, errors, changeHandler, submitHandler, touched} =
useForm(initialState, validations, onUpload);
return (
<div className='page'>
<div className='upload-card'>
<div id='preview'>
<img
src={preview || require('../../assets/user-solid.jpeg')}
id='image'
alt='Thumbnail'
className='user-post'
/>
</div>
</div>
<div className='upload-container'>
<div className='post-form-container'>
<p id='upload-form-label'>Hello, feel free to post an image!</p>
<form
// onSubmit={'return Validate(this);'}
onSubmit={submitHandler}
className='upload-form'
>
<div className='panel'>
<div className='button_outer'>
<div className='btn_upload'>
<input
filename={file}
onChange={(e) => onAddImage(e.target.files[0])}
type='file'
accept='.jpeg,.svg,.gif,.png'
id='image-selection-btn'
></input>
Choose your Art
</div>
</div>
</div>
<input
name='title'
type='text'
className='form-control'
placeholder='Enter Title'
id='cred-input'
required
value={values.title}
onChange={changeHandler}
/>
{touched.title && errors.title && (
<p className='error'>{errors.title}</p>
)}
<button type='submit' id='post-upload-btn' disabled={isValid}>
Upload Image
</button>
</form>
</div>
</div>
</div>
);
function isRequired(value) {
return value != null && value.trim().length > 0;
}
function isSame(value1, value2) {
return value1 === value2;
}
}
export default Uploads;
useForm.js:
import React, {useState} from 'react';
import {omit} from 'lodash';
function useForm(initialState = {}, validations = [], onSubmit = () => {}) {
// Add the 'onSubmit' argument
const {isValid: initialIsValid, errors: initialErrors} = validate(
validations,
initialState
);
const [values, setValues] = useState(initialState);
const [errors, setErrors] = useState(initialErrors);
const [isValid, setValid] = useState(initialIsValid);
const [touched, setTouched] = useState({});
const [file, setFile] = useState();
const [title, setTitle] = useState();
const changeHandler = (event) => {
const newValues = {...values, [event.target.name]: event.target.value};
const {isValid, errors} = validate(validations, newValues);
setValues(newValues);
setValid(isValid);
setErrors(errors);
setTouched({...touched, [event.target.name]: true});
};
// Add this
const submitHandler = (event) => {
event.preventDefault();
onSubmit(values);
};
return {values, changeHandler, isValid, errors, touched, submitHandler}; // Add 'submitHandler'
}
function validate(validations, values) {
const errors = validations
.map((validation) => validation(values))
.filter((validation) => typeof validation === 'object');
return {
isValid: errors.length === 0,
errors: errors.reduce((errors, error) => ({...errors, ...error}), {}),
};
}
export default useForm;
sample
const formData = new FormData();
formData.append('file', file);
formData.append('title', title);
const config = {
url: 'url',
data: formData,
headers: {
...formData.getHeaders() // <- formdata header.
}
}
axios.request(config)

How to hide a form after submit in react, currently you have to click a toggle button

Currently I am rendering a list of songs where there is a toggle button I made to render a form to add a song. How can I make it so when that form is submitted it will hide the form without a button click. I attempted to make a useEffect to trigger the function but I couldn't crack it. Thanks in advance.
The list of songs
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { deleteSong, getSongs, updateSong } from '../../store/song';
import ReactAudioPlayer from 'react-audio-player';
import { useHistory } from 'react-router';
import SongForm from '../AddSongForm';
import EditSongForm from '../EditSongForm';
import SpecificSong from '../SpecificSong';
const SongList = () => {
const [addShowForm, setAddShowForm] = useState(false);
// const [editShowForm, setEditShowForm] = useState(false);
const history = useHistory()
const dispatch = useDispatch();
const songsObj = useSelector((state) => state.songState.entries);
const songs = Object.values(songsObj)
const user = useSelector((state) => state.session.user);
const CurrentUserId = user?.id
const remove = (e) => {
dispatch(deleteSong(e.target.id));
}
const addFormCheck = (e) => {
if (addShowForm) setAddShowForm(false)
if (!addShowForm) setAddShowForm(true)
}
// const editFormCheck = (e) => {
// if (editShowForm) setEditShowForm(false)
// if (!editShowForm) setEditShowForm(true)
// }
useEffect(() => {
dispatch(getSongs());
}, [dispatch]);
return (
<div>
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm ?
<SongForm />
: null}
</div>
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div>
<SpecificSong id={id} songName={songName} songLink={songLink} userId={userId} />
</div>
))}
</ol>
</div>
);
};
export default SongList;
And the component that is being rendered
import { useState } from "react";
import { useDispatch } from "react-redux";
import { postSong } from "../../store/song";
import { useSelector } from "react-redux";
import Axios from 'axios'
const SongForm = () => {
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const [songSelected, setSongSelected] = useState("")
const reset = () => {
setSongName("");
setSongLink("");
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
let url;
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData()
formData.append('file', songSelected)
formData.append('upload_preset', 'd3gthd7l')
if (songSelected === '') {
setErrors(['You have to upload an audio file!'])
}
Axios.post("https://api.cloudinary.com/v1_1/dyhfkvy6u/video/upload", formData).then(async (response) => {
if (response.data.url) url = response.data.url
const newSong = {
songName,
songLink: url,
userId
};
const song = await dispatch(postSong(newSong))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
})
// reset();
};
return (
<div className="inputBox">
<h1>Add A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
<input
type='file'
onChange={(e) => { setSongSelected(e.target.files[0]) }}
placeholder="Song Link"
name="Audio File"
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default SongForm;
You could pass the setAddShowForm function to the form as a prop and update its state once submitted (Note that you can use && for conditional rendering):
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm && <SongForm setAddShowForm={setAddShowForm}/>}
</div>
Change your component declaration to
const SongForm = ({ setAddShowForm }) => {
And update the state in the handleSubmit method:
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('file', songSelected);
formData.append('upload_preset', 'd3gthd7l');
if (songSelected === '') {
setErrors(['You have to upload an audio file!']);
}
Axios.post(
'https://api.cloudinary.com/v1_1/dyhfkvy6u/video/upload',
formData
).then(async (response) => {
if (response.data.url) url = response.data.url;
const newSong = {
songName,
songLink: url,
userId,
};
const song = await dispatch(postSong(newSong)).catch(async (res) => {
const data = await res.json();
if (data && data.errors) setErrors(data.errors);
// Hide the form
setAddShowForm(false);
});
});
};
You cant trigger useEffect with a dispatch variable change. Dispatch is a hook and dont change once invoked. You need to create a state variable useState and change its value on handleChange, when you do that, include that variable on useEffect instead of dispatch and that will trigger the useEffect content.

Categories

Resources