File UserStore.js
import { makeAutoObservable } from "mobx";
export default class UserStore {
constructor() {
this._isAuth = false;
this._user = {};
makeAutoObservable(this);
}
setIsAuth(bool) {
this._isAuth = bool;
}
setUser(user) {
this._user = user;
}
get isAuth() {
return this._isAuth;
}
get user() {
return this._user;
}
}
File Auth.js
import React, {useContext, useState} from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import './../styles/auth.css';
import { LOGIN_ROUTE, REGISTRATION_ROUTE } from '../utils/consts';
import { observer } from 'mobx-react-lite';
import { Context } from '../index';
import { login, registration } from '../http/userApi';
const Auth = observer(() => {
const {user} = useContext(Context)
const location = useLocation()
const isLogin = location.pathname === LOGIN_ROUTE
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const click = async() => {
try {
let data;
if (isLogin) {
data = await login(email, password);
} else {
data = await registration(email, password);
}
user.setUser(data)
user.setIsAuth(true)
} catch (e) {
alert(e.response.data.message)
}
}
return (
<section className='section auth'>
<div className='container auth__wrapper'>
<div className='card'>
<form>
<h2>{isLogin ? 'Авторизация' : 'Регистрация'}</h2>
<input
type='email'
placeholder='Введите ваш email...'
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input
type='password'
placeholder='Введите ваш пароль...'
value={password}
onChange={e => setPassword(e.target.value)}
/>
<div className='btnandreg'>
{isLogin ?
<div className='text'>
Нет аккаунта? <NavLink className='NavLink underline' to={REGISTRATION_ROUTE}>Зарегистрируйтесь!</NavLink>
</div>
:
<div className='text'>
Есть аккаунт? <NavLink className='NavLink underline' to={LOGIN_ROUTE}>Войдите!</NavLink>
</div>
}
<button onClick={() => click()} className='form__btn'>{isLogin ? 'Войти' : 'Регистрация'}</button>
</div>
</form>
</div>
</div>
</section>
);
});
export default Auth;
File userApi.js
import { $authHost, $host } from "./index";
import jwt_decode from 'jwt-decode';
export const registration = async (email, password) => {
const {data} = await $host.post('api/user/registration', { email, password, role: 'ADMIN' })
return jwt_decode(data.token)
}
export const login = async (email, password) => {
const {data} = await $host.post('api/user/login', { email, password })
return jwt_decode(data.token)
}
export const check = async () => {
const response = await $host.post('api/auth/registration')
return response
}
Authorization passes, if in the case of successful authorization write alert(), it works, the problem is that the value of the variable isAuth does not change, and I assume that user.setUser(data) also does not work, but I can not be sure, I need to change the value of the variable isAuth to true, and also triggered user.setUser(data), repeat code runs successfully to this point, the problem is in these two lines or may be in another file`
Related
I'm using React and Firebase to create chat web app.
And I don't know how user can get notofications, when another user sent him a message in the chat.
Can someone tell me how to do it?
If you want to look at the whole project, here is github: https://github.com/PHILLyaHI/diplom-work/tree/main/React-Frontend/frontend
But for now, I’ll show the files below that I think are important for creating a notification system.
Input.js File with sending system
import React, { useContext, useState } from "react";
import Img from "../../static/chat/add-img.png";
import Attach from "../../static/chat/attach.png";
import { AuthContext } from "./AuthContext";
import { ChatContext } from "./ChatContext";
import {
arrayUnion,
doc,
serverTimestamp,
Timestamp,
updateDoc,
} from "firebase/firestore";
import { db, storage } from "../../firebase";
import { v4 as uuid } from "uuid";
import { getDownloadURL, ref, uploadBytesResumable } from "firebase/storage";
const Input = () => {
const [text, setText] = useState("");
const [img, setImg] = useState(null);
const { currentUser } = useContext(AuthContext);
const { data } = useContext(ChatContext);
const handleSend = async () => {
if (img) {
const storageRef = ref(storage, uuid());
const uploadTask = uploadBytesResumable(storageRef, img);
uploadTask.on(
(error) => {
//TODO:Handle Error
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then(async (downloadURL) => {
await updateDoc(doc(db, "chats", data.chatId), {
messages: arrayUnion({
id: uuid(),
text,
senderId: currentUser.uid,
date: Timestamp.now(),
img: downloadURL,
}),
});
});
}
);
} else {
await updateDoc(doc(db, "chats", data.chatId), {
messages: arrayUnion({
id: uuid(),
text,
senderId: currentUser.uid,
date: Timestamp.now(),
}),
});
}
await updateDoc(doc(db, "userChats", currentUser.uid), {
[data.chatId + ".lastMessage"]: {
text,
},
[data.chatId + ".date"]: serverTimestamp(),
});
await updateDoc(doc(db, "userChats", data.user.uid), {
[data.chatId + ".lastMessage"]: {
text,
},
[data.chatId + ".date"]: serverTimestamp(),
});
setText("");
setImg(null);
};
return (
<div className="input">
<input
type="text"
placeholder="Type something..."
onChange={(e) => setText(e.target.value)}
value={text}
/>
<div className="send">
<img src={Attach} alt="" />
<input
type="file"
style={{ display: "none" }}
id="file"
onChange={(e) => setImg(e.target.files[0])}
/>
<label title="IMAGE SUPPOSE TO BE LESS THAN 250KB" htmlFor="file">
<img src={Img} alt="" />
</label>
<button onClick={handleSend}>Send</button>
</div>
</div>
);
};
export default Input;
Chats.js The sidebar with users.
import React, { useContext, useEffect, useState } from "react";
import { doc, onSnapshot } from "firebase/firestore";
import { AuthContext } from "./AuthContext";
import { db } from "../../firebase";
import { ChatContext } from "./ChatContext";
import addNotification from "react-push-notification";
const Chats = () => {
const [chats, setChats] = useState([]);
const {currentUser} = useContext(AuthContext);
const { dispatch } = useContext(ChatContext)
useEffect(() => {
const getChats = () => {
const unsub = onSnapshot(doc(db, "userChats", currentUser.uid), (doc) => {
setChats(doc.data())
});
return () => {
unsub();
};
};
currentUser.uid && getChats()
}, [currentUser.uid]);
const handleSelect = (u) => {
dispatch({type: "CHANGE_USER", payload: u})
}
return (
<div className="chats">
{Object.entries(chats)?.map((chat) => (
<div className="userChat" key={chat[0]} onClick={()=>handleSelect(chat[1].userInfo)}>
{chats?.unread && (
<small className="unread">New</small>
)}
<img src={chat[1].userInfo.photoURL} alt=""/>
<div className="userChatInfo">
<span>{chat[1].userInfo.displayName}</span>
<p>{chat[1].lastMessage?.text}</p>
</div>
</div>
))};
</div>
);
};
export default Chats;
AuthContext.js
import { createContext, useEffect, useState } from "react";
import { auth } from "../../firebase";
import { onAuthStateChanged } from "firebase/auth";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState({});
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
console.log(user);
});
return () => {
unsub();
};
}, []);
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
};
import { createContext, useEffect, useState } from "react";
import { auth } from "../../firebase";
import { onAuthStateChanged } from "firebase/auth";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState({});
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
console.log(user);
});
return () => {
unsub();
};
}, []);
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
};
ChatContext.js
import {
createContext,
useContext,
useReducer,
} from "react";
import { AuthContext } from "./AuthContext";
export const ChatContext = createContext();
export const ChatContextProvider = ({ children }) => {
const { currentUser } = useContext(AuthContext);
const INITIAL_STATE = {
chatId: "null",
user: {},
};
const chatReducer = (state, action) => {
switch (action.type) {
case "CHANGE_USER":
return {
user: action.payload,
chatId:
currentUser.uid > action.payload.uid
? currentUser.uid + action.payload.uid
: action.payload.uid + currentUser.uid,
};
default:
return state;
}
};
const [state, dispatch] = useReducer(chatReducer, INITIAL_STATE);
return (
<ChatContext.Provider value={{ data:state, dispatch }}>
{children}
</ChatContext.Provider>
);
};
I have a react application where I have a username and password field which is then submitted to the backend(PHP) for creating and starting session. This is the code for Login.js:
import React, { useEffect, useState } from 'react'
import { useDispatch } from 'react-redux'
import { getResult } from "./redux/FormSlice"
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
function Login() {
const [username, setUsername] = useState("");//state variable for username
const [password, setPassword] = useState("");/state variable for password
let { result } = useSelector(state => state.FormSliceReducer)//result stores whether the authentication is successful or not
const navigate = useNavigate();
//the following useEffect is for reacting to changes whether the login is successful or not
useEffect(() => {
switch (result) {
case "success": {
const url = "/main/" + username
navigate(url)
break;
}
case "Invalid password": {
alert("Invalid password")
break;
}
case "Invalid email": {
alert("Invalid email")
break;
}
}
}, [result])
const dispatch = useDispatch();
const formSubmitted = (event) => {
event.preventDefault();
dispatch(getResult({username, password}))//when I submit the form I send username and password to the particular reducer and saga.
}
return (
<div>
<h1>Welcome to form</h1>
<form onSubmit={formSubmitted} method='POST' className='form'>
<label>Username: </label>
<input type="text" name="username" value={username} onChange={(event) => setUsername(event.target.value)}/>
<label>Password: </label>
<input type="password" name='password' value={password} onChange={(event) => setPassword(event.target.value)}/>
<input type="submit" value="Submit" name='submit'/>
</form>
</div>
)
}
export default Login
Whenever I click on submit I get the following error:
Error
FormSlice.js: Since I am using redux-toolkit I use createSlice function
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
result: ""
}
const slice = createSlice({
name: "FormSlice",
initialState,
reducers: {
getResult: (state, action) => {
},
setResult: (state, action) => {
//the message from the PHP backend comes here
state.result = action.payload.result[0].message
},
}
})
export const { getResult, setResult } = slice.actions
export default slice.reducer;
FormSaga.js:
import { put, takeEvery } from "redux-saga/effects"
function* getResult1(action) {
let url = "http://localhost:8787/php/form/form.php?username=" + action.payload.username + "&password=" + action.payload.password;
let result = yield fetch(url)
result = yield result.json()
yield put({type: "FormSlice/setResult", payload: result})
}
function* authSaga() {
yield takeEvery("FormSlice/getResult", getResult1);
}
export default authSaga;
Please comment if more information is needed.
i want to implement update user password form using react-router-dom v6 but this code is not working..
please please.. put your suggestion or explain me about my mistakes on this code.
userReducer.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_RESET,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const profileReducer = (state = {}, action) => {
switch (action.type) {
case UPDATE_PASSWORD_REQUEST:
return {
...state,
loading: true,
};
case UPDATE_PASSWORD_SUCCESS:
return {
...state,
loading: false,
isUpdated: action.payload,
};
case UPDATE_PASSWORD_FAIL:
return {
...state,
loading: false,
error: action.payload,
};
case UPDATE_PASSWORD_RESET:
return {
...state,
isUpdated: false,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
userAction.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const updatePassword = (passwords) => async (dispatch) => {
try {
dispatch({ type: UPDATE_PASSWORD_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.put(
`/api/v1/password/update`,
passwords,
config
);
dispatch({ type: UPDATE_PASSWORD_SUCCESS, payload: data.success });
} catch (error) {
dispatch({
type: UPDATE_PASSWORD_FAIL,
payload: error.response.data.message,
});
}
};
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
store.js
import {createStore,combineReducers,applyMiddleware} from 'redux';
import thunk from "redux-thunk";
import {composeWithDevTools} from "redux-devtools-extension";
import { profileReducer } from './Reducers/userReducer';
const reducer = combineReducers({
profile:profileReducer,
})
let initialState = {};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
UpdatePassword.js
import React, { Fragment, useState, useEffect } from "react";
import "./UpdatePassword.css";
import Loader from "../Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { clearErrors, updatePassword } from "../../Actions/userAction";
import { UPDATE_PASSWORD_RESET } from "../../Constants/userConstant";
import {useNavigate} from 'react-router-dom'
const UpdatePassword = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { error, isUpdated, loading } = useSelector((state) => state.profile);
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const updatePasswordSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("oldPassword", oldPassword);
myForm.set("newPassword", newPassword);
myForm.set("confirmPassword", confirmPassword);
dispatch(updatePassword(myForm));
};
useEffect(() => {
if (error) {
alert(error);
dispatch(clearErrors());
}
if (isUpdated) {
alert("Profile Updated Successfully");
navigate("/account");
dispatch({
type: UPDATE_PASSWORD_RESET,
});
}
}, [dispatch, error, isUpdated]);
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
{/* <MetaData title="Change Password" /> */}
<div className="updatePasswordContainer">
<div className="updatePasswordBox">
<h2 className="updatePasswordHeading">Update Profile</h2>
<form
className="updatePasswordForm"
onSubmit={updatePasswordSubmit}
>
<div className="loginPassword">
<input
type="password"
placeholder="Old Password"
required
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="New Password"
required
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<input
type="submit"
value="Change"
className="updatePasswordBtn"
/>
</form>
</div>
</div>
</Fragment>
)}
</Fragment>
);
};
export default UpdatePassword;
i want to make a form where user update user password.but due to any mistake this form is not working...
I need to test the register method along with the amplify auth signup which is written in my action file.
import history from "../utils/history.helper";
import { alertInfo } from "../utils/common.helper";
import { Auth } from "aws-amplify";
export const AuthAction = {
register,
};
function register(signUpData) {
return async (dispatch) => {
const {
username,
password,
company_name,
country_name,
designation,
} = signUpData;
try {
const signUpResponse = await Auth.signUp({
username,
password,
attributes: {
"custom:company_name": company_name,
"custom:country_name": country_name,
"custom:designation": designation,
},
});
alertInfo("success", "Please verify the mail,for successfull login");
history.push("/login");
} catch (error) {
alertInfo("error", error.message);
}
};
}]
This action file I'm calling from my signup component
import React from "react";
import { useForm } from "react-hook-form";
import { useDispatch } from "react-redux";
import { yupResolver } from "#hookform/resolvers/yup";
import content from "./content";
import { AuthInputField } from "../../common/FieldComponent";
import { AuthAction } from "../../../actions/auth.action";
import { signupSchema } from "../../common/Validation";
const SignupForm = () => {
const dispatch = useDispatch();
const { register, handleSubmit, errors } = useForm({
resolver: yupResolver(signupSchema),
});
const [buttonDisable, setButtonDisable] = React.useState(false);
function onSubmit(signUpData) {
setButtonDisable(true);
dispatch(AuthAction.register(signUpData));
}
return (
<div className="container" data-test="signUpContainer">
<h4>Welcome</h4>
<p>Please resigter to your account.</p>
<form data-testid="submit" onSubmit={handleSubmit(onSubmit)}>
{content.inputs.map((input, index) => {
return (
<AuthInputField
key={index}
name={input.name}
label={input.label}
placeholder={input.placeholder}
type={input.type}
register={register}
error={(errors && errors[input.name] && errors[input.name]) || {}}
/>
);
})}
<input
type="submit"
className="btn btn-primary button"
name="submit"
value={`Submit`}
role="submit"
disabled={buttonDisable}
/>
</form>
</div>
);
};
export default SignupForm;
I'm trying to find a way for testing the "Auth.signup" but didn't find any specific solution.
After spending so many hours, finally wrote these test cases for the above question.
import { register } from "./auth.action";
import mockData from "./__mocks__/mockData";
import { Auth } from "./__mocks__/aws-amplify";
import history from "../utils/history.helper";
jest.mock("./auth.action", () => {
return { register: jest.fn(() => mockPromise) };
});
jest.mock("aws-amplify");
describe("Signup action", () => {
beforeEach(() => {
register.mockClear();
});
test("Check register function have been called or not", async () => {
register();
expect(register).toHaveBeenCalled();
expect(register).toMatchSnapshot();
});
test("Check args passed in function are valid or not", () => {
expect(mockData.signupData).not.toBeNull();
expect(mockData.signupData).toMatchObject({
username: "Abhinav02#getnada.com",
});
expect(mockData.signupData).toHaveProperty("company_name", "Ces");
expect(mockData.signupData).toHaveProperty("country_name", "India");
expect(mockData.signupData).toHaveProperty("designation", "SD");
expect(mockData.signupData).toHaveProperty("password", "Password#123");
expect(mockData.signupData).toMatchSnapshot();
});
test("Amplify auth is called or not", () => {
Auth.signUp(mockData.signupData);
expect(Auth.signUp).toHaveBeenCalled();
expect(Auth.signUp).toMatchSnapshot();
});
test("history is pushed", () => {
const pushSpy = jest.spyOn(history, "push");
pushSpy("/login");
expect(pushSpy).toHaveBeenCalled();
expect(pushSpy.mock.calls[0][0]).toEqual("/login");
});
});
I have written the amplify auth test case in mock file.
// in __mocks__/aws-amplify.js
export const Auth = {
currentSession: jest.fn(() => Promise.resolve()),
signUp: jest.fn(() => Promise.resolve()),
signIn: jest.fn(() => Promise.resolve()),
};
Hope it helps others as well who are looking for the same.
I am building an artist search React app that hits the Ticketmaster API and should return the results before logging and after logging in.
I am getting 401(Unauthorized) after logging in.
search.js
import React, {Component} from 'react';
import axios from 'axios';
import {withRouter} from 'react-router-dom';
import {Form, FormControl, Button} from 'react-bootstrap';
import './style.css';
class SearchField extends Component {
state = {
search: ""
};
handleChange = (event) => {
const {name, value} = event.target;
this.setState({[name]: value.toLowerCase()});
};
apiCall = () => {
const ticketmasterURL = "https://app.ticketmaster.com/discovery/v2/events.json?keyword=";
const searchKey = process.env.REACT_APP_TM_KEY;
const term = this.state.search.split(" ").join("+");
axios.get("https://cors-anywhere.herokuapp.com/" + ticketmasterURL + term + "&apikey=" + searchKey)
.then(res => {
this.props.history.push({
pathname: "/events/",
search: `?${this.state.search.split(" ").join("+")}`,
state: {data: JSON.stringify(res.data._embedded.events)}
})
})
.catch(err => console.log(err));
};
handleSubmit = (event) => {
event.preventDefault();
this.apiCall();
//set the redirect state to true
this.setState({redirect: true});
};
render(){
return (
<div className="search-container">
<Form onSubmit={this.handleSubmit}>
<Form.Group>
<FormControl
type="text"
placeholder="Search"
name="search"
value={this.state.search}
onChange={this.handleChange}
/>
<div className="searchbtn-container">
<Button type="submit">Submit</Button>
</div>
</Form.Group>
</Form>
</div>
)
}
}
export default withRouter(SearchField);
app.js
import setAuthToken from './_helpers/setAuthToken';
import { setCurrentUser, logoutUser } from "./actions/authAction";
if (localStorage.jwtToken) {
const token = localStorage.jwtToken;
setAuthToken(token);
const decoded = jwt_decode(token);
Store.dispatch(setCurrentUser(decoded));
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
Store.dispatch(logoutUser());
window.location.href = "/login";
}
}
setAuthToken.js
import axios from 'axios';
const setAuthToken = (token) => {
if (token) {
axios.defaults.headers.common['Authorization'] = token;
} else {
delete axios.defaults.headers.common["Authorization"];
}
};
export default setAuthToken;
I think I localized the issue to setAuthToken function in app.js because it works without it but am not sure.
You need to add the type of the token, so:
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
https://www.rfc-editor.org/rfc/rfc6749 (Section 7.1)