Creating a custom React Hook - reactjs

I'm not very familiar with creating custom hooks, but what I would like to acheive is to create a function that takes in a file, which is then uploaded to firebase.
It would look like:
const UseUpload = (file) => {
const [fileInfo, setFileInfo] = useState({
fileUrl: '',
progress: 0,
error: '',
fileName: '',
})
const handleUpload = () => {
let file = e.target.files[0]
if(file) {
const storageRef = firebase
.storage()
.ref(`songs`)
.child(file.name)
const task = storageRef.put(file)
task.on(
"state_changes",
function progress(snap) {
setLoading(true);
const percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
setFileInfo({
...fileInfo,
progress: percentage
})
},
function error(err) {
setFileInfo({
...fileInfo,
error: err
})
},
function complete() {
storageRef.getDownloadURL().then((url) => {
setFileInfo({
...fileInfo,
fileUrl: url,
fileName: file.name
})
});
}
)
}
}
return fileInfo
}
SO basically what this does, is it takes in a file from an input type=file and then returns the fileInfo state.
Is there a way of achieving this?

Related

How to remove all file uploaded with upload component of ant design

Is there a way to delete all uploaded files at once? When I uploaded success. I want to call event remove all files. I didn't find any solution for this. Tks for all the help !
Here is my code:
upload file
const [fileDatas, setFileDatas] = useState([]);
const dummyRequest = ({ file, onSuccess }) => {};
const beforeUpload = (file, fileList) => {return false};
const { Dragger } = Upload;
const props = {
accept: 'image/png, image/jpeg, image/svg, image/gif, .xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf',
name: 'file',
multiple: true,
beforeUpload: beforeUpload,
action: dummyRequest,
onChange({ file, fileList }) {
if (file.status !== 'uploading') {
setFileUploads(fileList);
}
},
onDrop(e) {
console.log('Dropped files', e.dataTransfer.files);
setFileUploads(e.dataTransfer.files);
},
};
handle event submit upload file
const handleUploadFile = () => {
if (fileUploads.length === 0) {
toastActionWarning('No files to upload');
}else{
setFileUploads([]);
const formDatas = new FormData();
fileUploads.forEach((file) => {
formDatas.append(file.name, file.originFileObj);
});
axios
.post('files', formDatas, { withCredentials: true })
.then((res) => {
let newList = [...fileDatas];
res.data.data.forEach(element => {
element.author = window.userDisplayName;
newList = [element, ...newList]
});
setFileDatas(newList);
//I want to clear all file here
toastActionSuccess('Upload successfully');
})
.catch((error) => {
toastActionFailed('There was an error in processing');
});
}
};

How to add additional info to an image?

I am new to React and Ionic and builded the Ionic React photo gallery app with the help of this ionic tutorial. Now I also want to add additional info like its date, its location, some inputfields,... to the image when it is displayed but I just did not figure out how.
This is the code from the tutorial:
import { useState, useEffect } from "react";
import { isPlatform } from '#ionic/react';
import { Camera, CameraResultType, CameraSource, Photo } from '#capacitor/camera';
import { Filesystem, Directory } from '#capacitor/filesystem'
import { Storage } from '#capacitor/storage'
import { Capacitor } from '#capacitor/core';
import internal from "assert";
import { defaultMaxListeners } from "stream";
const PHOTO_STORAGE = "photos";
export function usePhotoGallery() {
const [photos, setPhotos] = useState<UserPhoto[]>([]);
useEffect(() => {
const loadSaved = async () => {
const {value} = await Storage.get({key: PHOTO_STORAGE });
const photosInStorage = (value ? JSON.parse(value) : []) as UserPhoto[];
// If running on the web...
if (!isPlatform('hybrid')) {
for (let photo of photosInStorage) {
const file = await Filesystem.readFile({
path: photo.filepath,
directory: Directory.Data
});
// Web platform only: Load the photo as base64 data
photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
}
}
setPhotos(photosInStorage);
};
loadSaved();
}, []);
const takePhoto = async () => {
const cameraPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100
});
const fileName = new Date().getTime() + '.jpeg';
const savedFileImage = await savePicture(cameraPhoto, fileName);
const newPhotos = [savedFileImage, ...photos];
setPhotos(newPhotos);
Storage.set({key: PHOTO_STORAGE,value: JSON.stringify(newPhotos)});
};
const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
let base64Data: string;
// "hybrid" will detect Cordova or Capacitor;
if (isPlatform('hybrid')) {
const file = await Filesystem.readFile({
path: photo.path!
});
base64Data = file.data;
} else {
base64Data = await base64FromPath(photo.webPath!);
}
const savedFile = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Data
});
if (isPlatform('hybrid')) {
// Display the new image by rewriting the 'file://' path to HTTP
// Details: https://ionicframework.com/docs/building/webview#file-protocol
return {
filepath: savedFile.uri,
webviewPath: Capacitor.convertFileSrc(savedFile.uri),
};
}
else {
// Use webPath to display the new image instead of base64 since it's
// already loaded into memory
return {
filepath: fileName,
webviewPath: photo.webPath
};
}
};
const deletePhoto = async (photo: UserPhoto) => {
// Remove this photo from the Photos reference data array
const newPhotos = photos.filter(p => p.filepath !== photo.filepath);
// Update photos array cache by overwriting the existing photo array
Storage.set({key: PHOTO_STORAGE, value: JSON.stringify(newPhotos) });
// delete photo file from filesystem
const filename = photo.filepath.substr(photo.filepath.lastIndexOf('/') + 1);
await Filesystem.deleteFile({
path: filename,
directory: Directory.Data
});
setPhotos(newPhotos);
};
return {
deletePhoto,
photos,
takePhoto
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
export async function base64FromPath(path: string): Promise<string> {
const response = await fetch(path);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
if (typeof reader.result === 'string') {
resolve(reader.result);
} else {
reject('method did not return a string')
}
};
reader.readAsDataURL(blob);
});
}
I managed to get the date and location with the Geolocation Plugin but I have no clue how to bind it to the taken photo...
I assume that I have to add the desired data via function base64FromPath() for web and const file = await Filesystem.readFile() for mobile? And afterwards display with the export interface UserPhoto part but I am completly lost and do not know where to start. Any suggestions welcome!
I found a way might not be the prettiest or directest but it seems to work for me.
Within the takePhoto function I get the location and date and then pass it on via the savePicture function. Inside the savePicture function I return latitude, longitude and time and therefore can access it through the loadSaved function within the photo object. Inside interface UserPhoto the 3 variables need also to be declared.
Here is the updated code if somebody would like to make a suggestion:
import { useState, useEffect } from "react";
import { isPlatform } from '#ionic/react';
import { Geolocation, Geoposition } from '#ionic-native/geolocation';
import { Camera, CameraResultType, CameraSource, CameraDirection, Photo } from '#capacitor/camera';
import { Filesystem, Directory } from '#capacitor/filesystem'
import { Storage } from '#capacitor/storage'
import { Capacitor } from '#capacitor/core';
import { stringify } from "querystring";
const PHOTO_STORAGE = "photos";
export function usePhotoGallery() {
const [photos, setPhotos] = useState<UserPhoto[]>([]);
useEffect(() => {
const loadSaved = async () => {
const {value} = await Storage.get({key: PHOTO_STORAGE });
const photosInStorage = (value ? JSON.parse(value) : []) as UserPhoto[];
// If running on the web...
if (!isPlatform('hybrid')) {
for (let photo of photosInStorage) {
const file = await Filesystem.readFile({
path: photo.filepath,
directory: Directory.Data
});
// Web platform only: Load the photo as base64 data
photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
}
}
setPhotos(photosInStorage);
};
loadSaved();
}, []);
const takePhoto = async () => {
const position = await Geolocation.getCurrentPosition();
const latitude = position.coords.latitude
const longitude = position.coords.longitude
const time = new Date(position.timestamp).toLocaleString()
const cameraPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
direction: CameraDirection.Rear,
quality: 100
});
const fileName = new Date().getTime() + '.jpeg';
const savedFileImage = await savePicture(cameraPhoto, fileName, latitude, longitude, time);
const newPhotos = [savedFileImage, ...photos];
setPhotos(newPhotos);
Storage.set({key: PHOTO_STORAGE,value: JSON.stringify(newPhotos)});
};
const savePicture = async (photo: Photo, fileName: string, latitude: number, longitude: number, time:string): Promise<UserPhoto> => {
let base64Data: string;
// "hybrid" will detect Cordova or Capacitor;
if (isPlatform('hybrid')) {
const file = await Filesystem.readFile({
path: photo.path!
});
base64Data = file.data;
} else {
base64Data = await base64FromPath(photo.webPath!);
}
console.log("base64Data")
console.log(base64Data)
let savedFile = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Data
});
console.log(savedFile)
if (isPlatform('hybrid')) {
// Display the new image by rewriting the 'file://' path to HTTP
// Details: https://ionicframework.com/docs/building/webview#file-protocol
return {
filepath: savedFile.uri,
webviewPath: Capacitor.convertFileSrc(savedFile.uri),
latitude: latitude,
longitude: longitude,
time: time
};
}
else {
// Use webPath to display the new image instead of base64 since it's
// already loaded into memory
return {
filepath: fileName,
webviewPath: photo.webPath,
latitude: latitude,
longitude: longitude,
time: time
};
}
};
const deletePhoto = async (photo: UserPhoto) => {
// Remove this photo from the Photos reference data array
const newPhotos = photos.filter(p => p.filepath !== photo.filepath);
// Update photos array cache by overwriting the existing photo array
Storage.set({key: PHOTO_STORAGE, value: JSON.stringify(newPhotos) });
// delete photo file from filesystem
const filename = photo.filepath.substr(photo.filepath.lastIndexOf('/') + 1);
await Filesystem.deleteFile({
path: filename,
directory: Directory.Data
});
setPhotos(newPhotos);
};
return {
deletePhoto,
photos,
takePhoto
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
latitude: number;
longitude: number;
time: string;
}
export async function base64FromPath(path: string): Promise<string> {
const response = await fetch(path);
const blob = await response.blob();
//const blob = new Blob(neu)
return new Promise((resolve, reject) => {
const reader = new FileReader();
console.log(reader)
reader.onerror = reject;
reader.onload = () => {
if (typeof reader.result === 'string') {
resolve(reader.result);
} else {
reject('method did not return a string')
}
};
reader.readAsDataURL(blob);
});
}
To access is in my UI.tsx I use eg {photo.latitude} to display the taken latitude value

How can i test downloading excel file with jest?

I am new to testing react components with jest and enzyme. I have this example
generateExcelFile = () => {
const {actions, state} = this.props;
const dateFrom = state.getIn(['config', 'marketingQuestionReport', 'dateFrom']);
const dateTo = state.getIn(['config', 'marketingQuestionReport', 'dateTo']);
this.setState({isLoading: true, isLoadingFinished: false});
fetch(`${env.MARKETING_QUESTION_REPORT}?dateFrom=${dateFrom}&dateTo=${dateTo}`)
.then((resp) => resp.blob())
.then((blob) => {
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(
blob,
`Marketing Question Report from ${dateFrom} to ${dateTo}.xlsx`
);
}
const url = window.URL.createObjectURL(blob);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = url;
tempLink.setAttribute(
'download',
`Marketing Question Report from ${dateFrom} to ${dateTo}.xlsx`
);
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}
document.body.appendChild(tempLink);
tempLink.click();
window.URL.revokeObjectURL(url);
toastr.success('Sucessfully generated marketing question report');
this.setState({isLoading: false, isLoadingFinished: true});
actions.clearMarketingQuestionReportDates();
})
.catch(() =>
toastr.error('An error occurred while generating marketing question report')
);
};
I am struck on .then part i don't know how to test after that the whole fetch call
What i have sofar
describe('<MarketingQuestionReportPage />', () => {
beforeEach(() => {
const fecthSpy = jest.spyOn(window, 'fetch').mockReturnValue(() =>
Promise.resolve({
blob: () =>
Promise.resolve({
size: 6682,
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}),
})
);
});
test('generateExcelFile', () => {
const props = {
state: fromJS({
config: {
marketingQuestionReport: {
dateFrom: '2019-01-01',
dateTo: '2021-03-21',
},
},
}),
actions: {
clearMarketingQuestionReportDates: jest.fn(),
},
};
const tree = shallowSetup(props);
tree.instance().generateExcelFile();
tree.fecthSpy.toHaveBeenCalled();
});
});
i maked a instance of my component - i got prepaired the needed props for that call to work and i am sending them to my instance.After that i call my method - generateExcelFile();
On my coverage everything is freen except the fetch call. I don't know how to fix this. Please help
You need to create a spy of fetch before rendering the component with jest.spyOn(object, methodName):
test('generateExcelFile', () => {
const fetchSpy = jest.spyOn(window, "fetch").mockReturnValue(Promise.resolve({
blob: () =>
Promise.resolve({
size: 6682,
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}),
})
);
//...
const tree = shallowSetup(props);
tree.instance().generateExcelFile();
expect(fetchSpy).toHaveBeenCalled();
});
In your test you can check if fecthSpy have been called

Reactjs - Firebase : Cancel Old Requests

I'm new to Firebase Realtime Database, and i'm trying to implement a search field that allow users to search for other users and view their profiles.
The Problem Is:
I want to make the search realTime(on each input change).but whenever a new request's sent, the old request is still working in the backend which's causing unexpected behavior,i've wrapped this functionality in a useEffect Hook,old sideEffects has to be cleaned up to make the query results predictable,how can i abort the previous request.
useSearchOwner Custom Hook:
const useSearchOwner = () => {
const [{ SearchValue, SearchResult, Search }, dispatch] = useReducer(
reducer,
{
SearchValue: "",
SearchResult: "",
Search: false,
}
);
const isFirstRender = useRef(true);
const onChangeHandler = (e) =>
dispatch({
type: ACTIONS.UPDATE_SEARCH_VALUE,
payload: { searchValue: e.target.value },
});
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
dispatch({ type: ACTIONS.START_SEARCHING });
const DispatchQueryByResult = async () => {
const ArrayOfOwners = await FirebaseUtilityInstance.SearchOwnerResult(
SearchValue
);
dispatch({
type: ACTIONS.UPDATE_SEARCH_RESULT,
payload: { searchResult: ArrayOfOwners },
});
dispatch({ type: ACTIONS.STOP_SEARCHING });
return () => {
FirebaseUtilityInstance.SearchOwnerCleanup();
};
};
DispatchQueryByResult();
}, [SearchValue]);
useEffect(() => {
console.log(SearchResult);
}, [SearchResult]);
return {
onChangeHandler: onChangeHandler,
Query: SearchValue,
QueryResult: SearchResult,
isSearching: Search,
};
};
Firebase Method To Do Query:
SearchOwnerResult = async (Query) => {
const { firstName, lastName } = getFirstNameAndLastName(Query);
let ArrayOfOwners = [];
await this.Database()
.ref("users")
.orderByChild("UserType")
.equalTo("owner")
.once("value", (snapshot) => {
const OwnersContainer = snapshot.val();
const keys = Object.keys(OwnersContainer);
for (let i = 0; i < keys.length; i++) {
const CurrentOwner = OwnersContainer[keys[i]];
if (
CurrentOwner.FirstName === firstName ||
CurrentOwner.LastName === lastName
) {
ArrayOfOwners.push(OwnersContainer[keys[i]]);
}
}
});
return ArrayOfOwners;
};

how to upload file like pdf or doc in firestore

I want to upload document files in firestore using redux. i get the file in and passed it as a state to the action file with other datas. following is my code in action file.
const createJob = (project) => {
return (dispatch, getState, {getFirebase, getFirestore}) => {
const firestore = getFirestore();
firestore.collection('Jobs').add({
...project,
postedby:'Employer1',
Documents:project.Documents.name
}).then(()=>{
dispatch({type:'CREATE_JOB', project});
}).catch((err)=>{
dispatch({type:'CREATE_JOB_ERROR', err});
})
}
};
but the data is saved as C:\fakepath\doc1.doc
how to upload the actual file in firestore
Technically you can upload an image to firestore. You need to convert it to base64 text first. Just spent some time figuring it out. I take the selected file from an upload file from browser then I upload it in the callback of the file reader. Hopefully this helps someone out.
function getBase64(file){
var n = document.getElementById('nameU').value;
//name of uploaded file from textbox
var d = document.getElementById('dateU').value;
//date of uploaded file from textbox
var reader = new FileReader();
reader.onerror = function (error) {
console.log('Error: ', error);
};
reader.readAsDataURL(file);
reader.onload = function () {
let encoded = reader.result.split(',');
//you split this to get mimetype out of your base64
addForSale(Date.now().toString(10), {uDesc: n, date: d, uFile: encoded[1]});
// I just used a timestamp as the ID
}
};
function addForSale(id, data) {
var collection = firebase.firestore().collection('forsale');
return collection.doc(id).set(data);}
Hi You cannot directly store the image in firestore.What you have to do is first store the document in firebase storage and get the url as reponse.Once the response is received add the url in documents.
First create an Storage Action in reduce:
import { storage } from '../../../firebase/firebase';
import {
ADD_DOCUMENT_STARTED,
ADD_DOCUMENT_COMPLETED,
ADD_DOCUMENT_ERROR
} from '../../actionTypes/storageActionTypes';
import { toast } from 'react-toastify';
import constants from '../../../config/constants';
export const addDocumentStart = () => ({
type: ADD_DOCUMENT_STARTED
});
export const addDocumentSuccess = () => ({
type: ADD_DOCUMENT_COMPLETED
});
export const addDocumentFailure = () => ({
type: ADD_DOCUMENT_ERROR
});
export const addDocument = (values, pathName) => {
const toastId = toast('Uploading Attachment, Please wait..', {
autoClose: false
});
return (dispatch) =>
new Promise(function(resolve, reject) {
dispatch(addDocumentStart());
const timeStamp = new Date().getTime();
const image = values.document[0];
var name;
if (values && values.documentName) {
name = timeStamp.toString().concat(values.documentName);
} else {
name = timeStamp.toString().concat(image.name);
}
const imageUpload = storage.ref(`${pathName}/${name}`).put(image);
imageUpload.on(
'state_changed',
(snapshot) => {
switch (snapshot.state) {
case 'paused':
reject('Upload is paused');
dispatch(addDocumentFailure('Upload is paused'));
break;
}
},
(error) => {
switch (error.code) {
case 'storage/unauthorized':
reject('Permission Denied');
dispatch(addDocumentFailure('Permission Denied'));
break;
case 'storage/canceled':
reject('Upload Cancelled');
dispatch(addDocumentFailure('Upload Cancelled'));
break;
case 'storage/unknown':
reject('Server Response Error');
dispatch(addDocumentFailure('Server Response Error'));
break;
}
},
() => {
toast.update(toastId, {
render: 'Attachment Uploaded successfully',
type: toast.TYPE.SUCCESS,
autoClose: constants.toastTimer
});
storage
.ref(pathName)
.child(name)
.getDownloadURL()
.then((url) => {
dispatch(addDocumentSuccess());
resolve(url);
});
}
);
});
};
Then in your onsubmit:
this.props.dispatch(addDocument(values, 'Jobs')).then((resp) => {
let documentUrl = resp;
firestore.collection('Jobs').add({
...project,
postedby:'Employer1',
Documents:documentUrl
}).then(()=>{
});

Resources