web3 staking function not displaying staked history in reactjs/solidity - reactjs

I created a simple dapp with react/truffle/web3 that allows users to enter a number of tokens and submit it to stake it, the problem that I got is that when clicked on submit button, Metamask popup to confirm the transaction(approuve) but I don't get the second popup to confirm stakeTokens function, hence, no transaction history displayed.
Dapp link: https://doxa-staking.netlify.app.
The stake smart contract code deployed on rinkeby: https://rinkeby.etherscan.io/address/0xAD015a006755b389d8e5BC2680cc5081dc1d0abd#code
The reactjs web3 code in github:
https://github.com/isofttechn/staking-dapp/blob/main/src/components/Home/index.js
My Stake Approval Function
approval = async () => {
const { web3, token, address } = this.props.wallet;
const tokenAmount = web3.utils.toWei("99999999", "ether");
const stakingContractAddress = process.env.REACT_APP_DOXACONTRACT_ADDRESS;
const approval = await token.methods
.approve(stakingContractAddress, tokenAmount)
.send({ from: address });
console.log(approval);
this.setState({ isApproved: true });
await this.checkAllowance();
};
My Stake Function:
stakeAZO = async () => {
this.setState({ stakeloading: true });
let type;
if (this.state.stakeType) {
type = 1;
} else type = 0;
if (this.state.stakeValue > 0 && this.state.yearsValue > 0) {
const { web3, staking, token, address } = this.props.wallet;
const tokenAmount = web3.utils.toWei(
this.state.stakeValue.toString(),
"ether"
);
var time = Date.now();
console.log("address", [
address,
process.env.REACT_APP_DOXACONTRACT_ADDRESS,
tokenAmount,
type,
this.state.yearsValue,
time,
]);
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
userAddress: address,
contractAddress: process.env.REACT_APP_DOXACONTRACT_ADDRESS,
amount: tokenAmount,
id: type,
noOfDays: this.state.yearsValue,
timestamp: time,
}),
};
const response1 = await fetch(
process.env.REACT_APP_API_URL + "/getsignature",
requestOptions
);
const data1 = await response1.json();
var signaturelkn = data1.result;
var sigtuplelkn = [
address,
process.env.REACT_APP_DOXACONTRACT_ADDRESS,
tokenAmount,
type,
this.state.yearsValue,
time,
signaturelkn,
];
try {
const stake = await staking.methods
.stake(tokenAmount, this.state.yearsValue, sigtuplelkn)
.send({ from: address });
this.setState({ stakeloading: false });
} catch (err) {
console.log(err);
this.setState({ stakeloading: false });
}
// console.log(stake);
this.getStakeRecords();
if (this.state.stakeType) {
this.getTokenBalance();
this.setState({ stakeloading: false });
}
} else {
this.setState({ stakeloading: false });
alert("Amount of AZO or days should be more than 0!");
}
};
How I rendered it:
<div className="d-block d-md-flex bottom-btn-cont">
{!this.state.isApproved && (
<button
className="btn-btn stake-btn"
onClick={() =>
this.props.wallet.connected
? this.approval()
: alert("Connect to wallet!")
}
>
Approve AZO
</button>
)}
<button
className="btn-btn stake-btn"
style={{
backgroundColor: this.state.isApproved
? "#002365"
: "#e2e2e2",
}}
disabled={!this.state.isApproved}
onClick={() =>
this.state.isApproved
? this.stakeAZO()
: alert("Approve tokens before staking!")
}
>
STAKE AZO{" "}
{this.state.stakeloading ? (
<img
style={{ width: "20px", height: "20px" }}
src={buyLoader}
></img>
) : null}
</button>
</div>

Related

REACT - POST API route on likes and dislikes

I'm having some trouble figuring out the answer of the following issue, if any of you could help, that would be great :
I've set up my routes and the API works fine. One of those routes consists of liking and disliking posts, the method for those likes and dislikes being POST.
My console does not show any error and even responds to logs inside the function.
My problems are the following:
First I'm wondering if I can retrieve previously made likes or dislikes without having to create a get route. (if that makes sense, sorry I'm fairly new.)
Secondly, well, managing to POST those likes and dislikes.
Here's the code.
Frontend.
const currentId = localStorage.getItem('userId');
const params = useParams();
const [post, setPost] = useState({
title: '',
description: '',
image: null,
likes: '',
dislikes: '',
usersLiked: [],
usersDisliked: [],
});
useEffect(() => {
axios
.get('http://localhost:5000/api/posts/' + params.id, config)
.then((response) => {
setPost(response.data);
});
}, []);
const [likeState, setLikeState] = useState(0);
const usersLiked = post.usersLiked;
const usersDisliked = post.usersDisliked;
function likef() {
if (likeState === 0 && usersLiked.userId != currentId) {
setLikeState(1);
usersLiked.push(currentId);
post.likes = post.likes + 1;
saveLikes(post.likes);
} else if (likeState === 1) {
setLikeState(0);
usersLiked.shift();
post.likes = post.likes - 1;
}
}
function dislikef() {
if (likeState === 0 && usersDisliked.userId != currentId) {
setLikeState(-1);
usersDisliked.push(currentId);
post.dislikes = post.dislikes + 1;
saveLikes(post.dislikes);
} else if (likeState === -1) {
setLikeState(0);
usersDisliked.shift();
post.dislikes = post.dislikes - 1;
saveLikes(post.dislikes);
}
}
const saveLikes = () => {
const id = post._id;
const url = `http://localhost:5000/api/posts/${id}/like`;
fetch(url, {
method: 'POST',
headers: { Authorization: `Bearer ${AbstractManager.token}` },
})
.then(() => {
setLikeState(response.data);
})
.catch((e) => {});
};
Template code
<div className="like">
<button
id="btn-pink"
onClick={likef}
className={[
likeState === 1 ? 'btn-pink' : null,
'btn-pink',
].join()}
>
<FontAwesomeIcon icon={faHeart} /> {post.likes}
</button>
<button
id="btn-red"
onClick={dislikef}
className={[
likeState === -1 ? 'btn-red' : null,
'btn-red',
].join()}
>
<FontAwesomeIcon icon={faHeartBroken} /> {post.dislikes}
</button>
If any one of you could help, that would be awesome. Thank you in advance.

Route not clearing when setting new route

I'm using Google Maps Directions API to draw routes on a map. It does what I want on the first call of DirectionsRenderer.setDirections(response), but on the second call, it persists the previous route and uses the new one on top of it. How can I clear the previous route?
Code:
export async function testRouteCalculation(
directionsService: google.maps.DirectionsService,
directionsRenderer: google.maps.DirectionsRenderer,
withWaypoints: boolean,
numWaypointsToInclude: number
) {
let request: google.maps.DirectionsRequest = {
origin: testOrigin,
destination: testDestination,
travelMode: google.maps.TravelMode["DRIVING"],
unitSystem: google.maps.UnitSystem.METRIC,
provideRouteAlternatives: false,
// region is specified for region biasing
region: "za",
waypoints: [],
};
if (withWaypoints) {
for (let i = 0; i < numWaypointsToInclude; i++) {
request!.waypoints!.push(testWaypoints[i]);
}
}
try {
const response = await directionsService.route(request);
return response;
} catch (err) {
throw err;
}
The map component:
const Map = () => {
const ref = React.useRef<HTMLDivElement>(null);
const [map, setMap] = React.useState<google.maps.Map>();
const [directionsRenderer, setDirectionsRenderer] =
React.useState<google.maps.DirectionsRenderer>();
const [directionsService, setDirectionsService] =
React.useState<google.maps.DirectionsService>();
React.useEffect(() => {
let newMap = null;
if (ref.current && !map) {
newMap = new window.google.maps.Map(ref.current, {
center: capeTownCoordinates,
zoom: 13,
streetViewControl: false,
mapTypeControl: false,
});
setMap(newMap);
}
const newDirectionsRenderer = new google.maps.DirectionsRenderer();
newDirectionsRenderer.setMap(newMap);
setDirectionsRenderer(newDirectionsRenderer);
setDirectionsService(new google.maps.DirectionsService());
}, [ref, map]);
if (map && directionsRenderer && !directionsRenderer.getMap()) {
directionsRenderer.setMap(map);
}
const handleClick = async () => {
if (directionsRenderer && directionsService) {
try {
const response = await testRouteCalculation(
directionsService,
directionsRenderer,
true,
2
);
directionsRenderer.setDirections(response);
} catch (err) {
console.log(err);
}
} else {
console.log("no directionsRenderer or directionsService object");
}
};
return (
<>
<div id="map" style={{ height: "300px", width: "400px" }} ref={ref}></div>
<button onClick={handleClick} className={styles["floating-button"]}>
Get route
</button>
</>
);
};
I searched up and saw proposed solutions like directionsRenderer.setDirections(null) or directionsRenderer.setMap(null) before setting the new directions, and a couple of others, but none of them worked for me. I would think that .setDirections() would overwrite previous routes, but it seems that the routes drawn on the map and the directions stored in the directionRenderer are decoupled.
I found that calling directionsRenderer({routes: []}) achieved what I was looking for.

PUT Request - Uploading a file - React / Express / Multer - req.file = undefined

I'm developping a social network for a school project, and I want to allow the user to update his/her information, specifically the profile Photo and the cover Photo.
I use multer for storing images.
When I try to upload an image using a POST request, it works perfectly fine but on a PUT request it says req.file /req.files is always undefined.
// FORM (IMPORTANT PART)
<form
className="update__form"
onSubmit={handleSubmit}
encType="multipart/form-data"
id="form"
>
{/* GESTION PHOTO DE COUVERTURE */}
<div className="update__form-cover">
<input
type="file"
name="coverPhotoUrl"
className="update__form-cover-input"
id="cover"
accept="image/*"
onChange={handleCover}
/>
<div className="update__form-cover-button">
Modifier la photo de couverture
</div>
</div>
<div
className={
loadCover === true
? 'update__form-cover-img'
: 'update__form-cover-img--close'
}
>
<img id="coverImg" alt="ok" />
</div>
{/* GESTION PHOTO DE PROFIL */}
<div className="update__form-profile">
<input
type="file"
name="profilePhotoUrl"
className="update__form-profile-input"
id="profile"
accept="image/*"
onChange={handleProfile}
/>
<div className="update__form-profile-button">
Modifier la photo de profil
</div>
</div>
<div
// MY DIFFERENTS FUNCTIONS
// TO DISPLAY AND STORE THE NEW COVER (USESTATE)
const handleCover = () => {
const coverChange = document.getElementById('cover').files
if (coverChange.length > 0) {
const fileReader = new FileReader()
fileReader.onload = function (event) {
document
.getElementById('coverImg')
.setAttribute('src', event.target.result)
setLoadCover(true)
setData({
...data,
coverPhotoUrl: coverChange[0],
})
}
fileReader.readAsDataURL(coverChange[0])
}
}
// DISPLAY AND STORE THE NEW PROFILE PHOTO (USESTATE)
const handleProfile = () => {
const profileChange = document.getElementById('profile').files
setData({
...data,
profilePhotoUrl: profileChange[0].name,
})
if (profileChange.length > 0) {
const test = new FileReader()
test.onload = function (event) {
document
.getElementById('profileImg')
.setAttribute('src', event.target.result)
setLoadProfile(true)
}
test.readAsDataURL(profileChange[0])
}
}
// FUNCTION CALLED WHEN FORM IS SUBMITTED
const handleSubmit = (event) => {
event.preventDefault()
try {
updateUser(data)
} catch (err) {
console.log(err)
}
}
// FUNCTION TO FETCH PUT
const updateUser = (data) => {
console.log(data)
const userId = localStorage.getItem('userId')
fetch('http://localhost:8000/api/user/' + userId, {
method: 'PUT',
headers: {
'Content-Type': 'form-data',
},
body: JSON.stringify(data),
})
}
export default updateUser
// BACK CONFIG
const multer = require('multer');
const MIME_TYPES = {
'image/jpg': 'jpg',
'image/jpeg': 'jpeg',
'image/png': 'png',
'image/svg': 'svg',
}
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, '../images')
},
filename: (req, file, callback) => {
const name = file.originalname.split(' ').join('_');
const extension = MIME_TYPES[file.mimetype];
callback(null, name + Date.now() + '.' + extension);
}
});
const upload = multer({ storage: storage });
router.put('/:id', upload.array(), userCtrl.updateUser);
// CONTROLLER (not very important HERE BUT RETURN REQ.FILE UNDEFINED)
exports.updateUser = ((req, res, next) => {
console.log(req.file)
console.log(req.files)
const userInfos = req.file ? {
...JSON.parse(req.body.data),
coverPhotoUrl: `${req.protocol}://${req.get('host')}/images/${req.file.filename}`
} : {
...req.body
};
delete userInfos._userId;
User.findOne({
_id: req.params.id
})
.then((user)=> {
User.updateOne({
_id: req.params.id
},
{
...userInfos,
_id: req.params.id
})
.then(()=> res.status(200).json({ message : 'infos mises à jour ! '}))
.catch((error)=> res.status((401)).json({ error }));
})
.catch((error)=> res.status(400).json({ error }));
});
If someone come on this, i've finally found the answer :
first : use the formData constrcutor form-data-infos mdn, to put your datas in (formdata.append())
second : on the function who will fetch (front to back) : just remove the 'content-type', the browser will automatically set this,
it should run normally then
I also want to apologize for this 'useless' post because all the answers were on this forum (and on the web), my eyes were just closed

scrollIntoView does not work on first load and after each refresh

I am seeing a very strange behavior in my chat. Once the chat is opened, the scroll moves only a tiny bit down only when there are images in the chat. When there is only text, it goes all the way down. Also, if I close the chat and open it again, the scroll goes all the way down regardless of the content. However, If I refresh the page, the scroll returns to its weird behavior. I am puzzled as to why this is happening. Here's my code:
Here's how the chat starts:
startChat () {
document.getElementById("myForm").style.display = "block";
const ref = firebase.firestore().collection('Chats').doc(this.state.uid).collection('Messages');
const query = ref.orderBy('timestamp', 'desc').limit(10)
this.unsubFromMessages = query.onSnapshot((snapshot) => {
if (snapshot.empty) {
console.log('No matching documents.');
firebase.firestore().collection('Chats').doc(this.state.uid).
set({
name: this.state.displayName,
uid: this.state.uid,
email: this.state.email
}).then(console.log("info saved"))
.catch((error) => {
console.log("Error saving info to document: ", error);
});
}
snapshot.docChanges().reverse().forEach((change) => {
if (change.type === 'removed') {
console.log(change.doc.data().content)
}
else if (change.type === 'added') {
this.setState(state => {
const messages = [...state.messages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
}
else if (change.type === 'modified') {
const filteredMessages = this.state.messages.filter(message => message.body.allowed === "yes")
this.setState(state => {
const messages = [...filteredMessages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
}
});
}, (error) => {console.log(error)});
}
Here's the scroll function:
scrollToBottom = () => {
this.myRef.current.scrollIntoView({ behavior: "smooth" });
}
Here's the JSX of the chat:
<div className="form-popup" id="myForm">
<form className="form-container" onSubmit={this.chatFormSubmit}>
<h1>Chat</h1>
<label htmlFor="msg"><b>Message</b></label>
<div className="chatArea" id='messages'>
{
this.state.messages.map((message, index) => {
return message.body.uid === this.state.uid && !message.body.imageUrl
?
<p className="message-sent" key={index}>{message.body.content}</p>
:
message.body.uid === this.state.uid && message.body.imageUrl
?
<img src={message.body.imageUrl} className="message-sent" key={index}></img>
:
<p className="message-received" key={index}>{message.body.content}</p>
})
}
<div style={{ float:"left", clear: "both" }}
ref={this.myRef}>
</div>
</div>
And if the functions for closing and submitting messages to the chat are of any use, here they are:
closeForm() {
document.getElementById("myForm").style.display = "none";
this.setState({messages: []})
this.unsubFromMessages();
}
chatFormSubmit(e) {
e.preventDefault();
this.setState({ writeError: null });
firebase.firestore()
.collection('Chats')
.doc(this.state.uid)
.collection('Messages')
.doc()
.set({
docId: this.state.docId,
content: this.chatArea.current.value,
allowed: "yes",
timestamp: new Date(),
uid: this.state.uid,
name: this.state.displayName,
email: this.state.email
}, { merge: true })
.catch((error) => {
this.setState({ writeError: error.message });
})
.then(this.chatArea.current.value = '')
}
Again, I figured it out myself. Instead of calling "this.scrollToBottom()" in setTimeout, I should have simply passed it like this setTimeout( this.scrollToBottom, 2000). That is why setTimeout was not working and the scroll stopped half way. Credit goes to Felix Kling's comment in ReactJS: setTimeout() not working?.

Enable/disable submit button base on useState boolean values changes true/false using react jest testing library

I am populating the form fields from a configuration and after user updates the fields, I compare the updated fields with configuration fetched earlier using "isEqual". If "isEqual = false" I enable the submit button. I am having little trouble to simulate this behavior using jest. Could anyone help with this test case?
Below is my sample code snippets:
const [areValuesEqual, setAreValuesEqual] = React.useState(true);
const onSubmit = React.useCallback(values => {
if (!isEqual(originalValues, values)) {
console.log('submitted.');
props.handleNext(values);
}
console.log("didn't submit.");}, [props, originalValues]);
useEffect(() => setAreValuesEqual(isEqual(originalValues, formik.values)), [formik.values, originalValues]);
<div>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
data-testid="next"
disabled={areValuesEqual}
>
Next
</Button>
</div>
Here is my test:
describe('FormComponent', () => {
const Spec = {
appName: 'somerepo',
repo: 'project-somerepo',
proxies: [
{
env: {
dev: {
basepath: '/somerepo-dev.net',
target: 'https://somerepo-dev.net',
},
test: {
basepath: '/somerepo-test.net',
target: 'https://somerepo-test.net',
}
},
},
],
};
function renderFormComponent() {
return render(
<ManageFormComponent metadata={metadata} handleNext={() => { }} />
);
}
it.only('should render form', async () => {
await act(async () => {
renderFormComponent();
await Promise.resolve();
});
expect(screen).toBeDefined();
expect(screen.getByText('Project Repo')).toBeInTheDocument();
const Repo = await screen.findByTestId('Repo');
const RepoInput = Repo.querySelector('input')!;
RepoInput.focus();
fireEvent.change(document.activeElement!, {
target: { value: 'project-somerepo' },
});
fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' });
fireEvent.keyDown(document.activeElement!, { key: 'Enter' });
expect(await screen.findByText('I want to')).toBeInTheDocument();
const deploy = await screen.findByTestId('deployment');
const deployInput = deploy.querySelector('input')!;
deployInput.focus();
fireEvent.change(document.activeElement!, {
target: { value: 'promote service' },
});
fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' });
fireEvent.keyDown(document.activeElement!, { key: 'Enter' });
expect(
await screen.findByText('Select an environment to deploy'),
).toBeInTheDocument();
const env = await screen.findByLabelText('TEST');
userEvent.click(env);
const Target = await screen.findByText('Target Endpoint')
expect(Target).toBeInTheDocument();
expect(await screen.findByTestId('target')).toHaveValue(
Spec.proxies[0].env.dev.target,
);
const Basepath = await screen.findByText('BasePath')
expect(Basepath).toBeInTheDocument();
expect(await screen.findByTestId('basepath')).toHaveValue(
Spec.proxies[0].env.dev.basepath,
);
const TargetInput = Target.querySelector('input')
TargetInput?.focus();
fireEvent.change(document.activeElement!, {
target: { value: 'https://somerepo-test.net' }
})
const BasepathInput = Basepath.querySelector('input')
BasepathInput?.focus();
fireEvent.change(document.activeElement!, {
target: { value: '/somerepo-test.net' }
})
const nextButton = await screen.findByRole('button', { name: /Next/ });
expect(nextButton).toBeEnabled();
});
}); ```
I get below error, which i believe is happening due to useEffect hook, where I compare the values of fetched values and updated values. If they are not equal, then submit button is enabled. For example below are the JSON objects are being compared:
expect(element).toBeEnabled()
Received element is not enabled:
<button class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-button-6 MuiButton-containedPrimary Mui-disabled Mui-disabled" data-testid="next" disa
bled="" tabindex="-1" type="submit" />
336 |
337 | // expect(nextButton).toBeTruthy();
> 338 | expect(nextButton).toBeEnabled();
| ^
339 | });

Resources