Mern Social Network الجزء السابع
فهاد الجزء السابع من Mern Social Network غادي نكملوا الجزء الخاص بالمستخدم وغادي نزيدو ل component لي غادي يمكن من عرض المستخدمين كاملين ولي غادي تكون عندي إمكانية متابعتهم وغادي نزيدو ل component لي غادي يمكن المستخدم باش يشوف البروفيل ديالو وأيضا تعديل المعلومات الخاصة به.
نظرة سريعة بالفيديو
1- إضافة ل component Users
دائما ف dossier pages زيد fichier Users.js لي فيه غادي نعرضوا المستخدمين كاملين ولي غادي تكون عندي إمكانية متابعتهم وهاد الخاصية غادي نزيدو الكود ديالها فالأجزاء القادمة.
الكود ديال الملف هو :
import React from "react";
import { Link } from "react-router-dom";
import { useDispatch, connect } from "react-redux";
import { getAllUsers } from "../redux/actions/userActions";
import { isLogged } from "../helpers/auth";
function Users({ users, userError }) {
const dispatch = useDispatch();
const [error, setError] = React.useState(null);
const jwt = isLogged();
React.useEffect(() => {
if (userError && userError !== null) {
setError(userError);
}
dispatch(getAllUsers(jwt && jwt.token));
}, [dispatch, jwt, userError]);
function showError() {
return error && <div className="alert alert-danger">{error}</div>;
}
if (!jwt) {
return (
<div className="container">
<div className="row my-5">
<div className="col-md-8 mx-auto">
<div className="alert alert-info">
<Link to="/login">Connectez vous pour voir les profiles</Link>
</div>
</div>
</div>
</div>
);
}
return (
<div className="container">
<div className="row my-4">
<div className="col-md-8 mx-auto">
{showError()}
<div className="card p-2">
<div className="card-header bg-white">
<h3 className="card-title">Profiles</h3>
</div>
<div className="card-body">
<ul className="list-group">
{users &&
users.map((user, index) => (
<Link
to={`/user/${user._id}`}
key={index}
style={{ textDecoration: "none" }}
>
<li className="list-group-item d-flex flex-row justify-content-between align-items-center">
<div className="d-flex flex-row justify-content-between align-items-center">
<img
src={`http://localhost:8888/api/user/photo/${user._id}`}
width="50"
height="50"
className="img-fluid rounded pr-2"
alt={user && user.name}
/>
<h4 className="mt-3">{user.name}</h4>
</div>
<i className="fa fa-arrow-right"></i>
</li>
</Link>
))}{" "}
</ul>
</div>
</div>
</div>
</div>
</div>
);
}
const mapStateToProps = ({ user: { users, userError } }) => ({
users,
userError,
});
export default connect(mapStateToProps, null)(Users);
2- إضافة ل component Profile
دائما ف dossier pages زيد fichier Profile.js لي فيه غادي يمكن للمستخدم باش يشوف البروفيل ديالو والناس لي متبعهم ولي متبعينوا بالإضافة ل tweets لي زاد وروابط تعديل وحذف الحساب ديالو.
الكود ديال الملف هو :
import React from "react";
import { useParams, Link, useHistory } from "react-router-dom";
import { isLogged, checkAuth, logout } from "../helpers/auth";
import { getUser, deleteUser } from "../redux/actions/userActions";
import FollowButton from "../components/FollowButton";
import FollowComponent from "../components/FollowComponent";
import { useDispatch, connect } from "react-redux";
import PostList from "../components/PostList";
import { getUserPosts } from "../redux/actions/postActions";
function Profile({ userSuccess, userError, userPosts }) {
const { userId } = useParams();
const [error, setError] = React.useState("");
const [following, setFollowing] = React.useState(false);
const [user, setUser] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const jwt = isLogged();
const dispatch = useDispatch();
const history = useHistory();
const date = user && user.createdAt ? new Date(user.createdAt) : null;
React.useEffect(() => {
async function getProfile() {
const userData = await getUser(userId, jwt && jwt.token);
if (userData.error) {
setError(userData.error);
} else {
setUser(userData.data);
setFollowing(checkFollow(userData.data));
}
}
function checkFollow(user) {
const match = user.followers.find((follower) => {
return follower._id === jwt.user._id;
});
return match;
}
if (loading) {
getProfile();
}
return () => {
setLoading(false);
};
}, [userId, jwt, loading]);
React.useEffect(() => {
if (userSuccess) {
logout(() => {
history.push("/");
});
dispatch({ type: "TOGGLE_SUCCESS" });
}
if (userError) {
setError(userError);
}
function loadUserPosts() {
dispatch(getUserPosts(jwt.token, userId));
}
loadUserPosts();
}, [userError, userSuccess, dispatch, userId]);
function showError() {
return error && <div className="alert alert-danger">{error}</div>;
}
function hanldeButtonClick(user) {
setUser(user);
setFollowing(!following);
}
if (!jwt) {
return (
<div className="container">
<div className="row my-5">
<div className="col-md-8 mx-auto">
<div className="alert alert-info">
<Link to="/login">Connectez vous pour voir le profile</Link>
</div>
</div>
</div>
</div>
);
}
return (
<div className="container">
<div className="row my-5">
<div className="col-md-8 mx-auto">
{error ? (
showError()
) : (
<div className="card p-2">
<div className="d-flex flex-row justify-content-start align-items-center">
<img
src={`http://localhost:8888/api/user/photo/${userId}?${new Date().getTime()}`}
width="50"
height="50"
className="img-fluid rounded pr-2"
alt={user && user.name}
/>
<h4 className="mt-3">{user && user.name}</h4>
</div>
<span className="font-weight-bold mt-3">
{user && user.email}
</span>
<p className="lead">{user && user.about}</p>
<hr />
<span className="badge badge-danger col-4">
Inscrit le : {date && date.toLocaleDateString()}
</span>
<hr />
{checkAuth(userId) ? (
<div className="d-flex flex-row justify-content-end align-items-center">
<Link to={`/user/edit/${userId}`}>
<i className="fa fa-edit btn mr-2 btn-warning btn-sm"></i>
</Link>
<Link
to="#"
onClick={() => dispatch(deleteUser(userId, jwt.token))}
>
<i className="fa fa-trash btn btn-danger btn-sm"></i>
</Link>
</div>
) : (
<FollowButton
following={following}
hanldeButtonClick={hanldeButtonClick}
token={jwt && jwt.token}
followId={user && user._id}
userId={jwt && jwt.user._id}
/>
)}
<hr />
<h3 className="text-primary">Abonnés</h3>
<hr />
<FollowComponent data={user && user.followers} />
<h3 className="text-primary">Abonnements</h3>
<hr />
<FollowComponent data={user && user.following} />
<hr />
<h3 className="text-primary">Publications</h3>
<PostList posts={userPosts} />
</div>
)}
</div>
</div>
</div>
);
}
const mapStateToProps = ({
user: { userError, userSuccess },
post: { userPosts },
}) => ({
userError,
userSuccess,
userPosts,
});
export default connect(mapStateToProps, null)(Profile);
3- إضافة ل component EditProfile
دائما ف dossier pages زيد fichier EditProfile.js لي فيه غادي يمكن للمستخدم باش يدير تعديل للمعلومات الخاصة به.
الكود ديال الملف هو :
import React from "react";
import { useDispatch, connect } from "react-redux";
import { useParams, useHistory } from "react-router-dom";
import { isLogged, checkAuth } from "../helpers/auth";
import { getUser } from "../redux/actions/userActions";
import { updateUser } from "../redux/actions/userActions";
// import { createUser } from "../redux/actions/userActions";
function EditProfile({ userError, userSuccess }) {
const [user, setUser] = React.useState({
name: "",
email: "",
password: "",
about: "",
image: "",
});
const [error, setError] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const dispatch = useDispatch();
const history = useHistory();
const jwt = isLogged();
const { userId } = useParams();
const userData = new FormData();
React.useEffect(() => {
async function getProfile() {
const userData = await getUser(userId, jwt && jwt.token);
if (userData.error) {
setError(userData.error);
} else {
setUser(userData.data);
}
}
if (loading) {
getProfile();
}
return () => {
setLoading(false);
};
}, [userId, jwt, loading]);
React.useEffect(() => {
if (userSuccess) {
history.push(`/user/${user && user._id}`);
dispatch({ type: "TOGGLE_SUCCESS" });
}
if (userError) {
setError(userError);
}
}, [userError, userSuccess, history,user, dispatch]);
function handleInputChange(event) {
const value =
event.target.name === "image"
? event.target.files[0]
: event.target.value;
setUser({ ...user, [event.target.name]: value });
}
function showError() {
return error && <div className="alert alert-danger">{error}</div>;
}
function handleFormSubmit(event) {
event.preventDefault();
user.name && userData.append("name", user.name);
user.email && userData.append("email", user.email);
user.password && userData.append("password", user.password);
user.about && userData.append("about", user.about);
user.image && userData.append("image", user.image);
dispatch(updateUser(userData, jwt.token, userId));
}
if (!checkAuth(userId)) {
history.push(`/user/${userId}`);
}
return (
<div className="container">
<div className="row my-5">
<div className="col-md-6 mx-auto">
{showError()}
<h3 className="card-title">Modifier mon profile</h3>
<form
onSubmit={(event) => handleFormSubmit(event)}
className="card p-2"
>
<div className="form-group">
<img
width="200"
height="200"
src={`http://localhost:8888/api/user/photo/${userId}`}
alt={user && user.name}
className="img-fluid rounded"
/>
</div>
<div className="form-group">
<input
type="file"
accept="image/*"
name="image"
onChange={(event) => handleInputChange(event)}
className="form-control"
/>
</div>
<div className="form-group">
<input
type="text"
name="name"
placeholder="Nom & Prénom"
value={user.name}
required
onChange={(event) => handleInputChange(event)}
className="form-control"
/>
</div>
<div className="form-group">
<input
type="email"
name="email"
placeholder="Email"
required
value={user.email}
onChange={(event) => handleInputChange(event)}
className="form-control"
/>
</div>
<div className="form-group">
<input
type="password"
name="password"
placeholder="Mot de passe"
required
value={user.password || ""}
onChange={(event) => handleInputChange(event)}
className="form-control"
/>
</div>
<div className="form-group">
<textarea
row="5"
cols="30"
name="about"
placeholder="Bio"
required
value={user.about}
onChange={(event) => handleInputChange(event)}
className="form-control"
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-outline-primary">
Valider
</button>
</div>
</form>
</div>
</div>
</div>
);
}
const mapStateToProps = ({ user: { userError, userSuccess } }) => ({
userError,
userSuccess,
});
export default connect(mapStateToProps, null)(EditProfile);
4- إضافة الصفحة الرئيسية
دائما ف dossier pages زيد fichier Home.js لي فيه غادي يتعرضوا les tweets ديال المستخدمين لي متبعهم الشخص لي connecté وأيضا غادي تكون عندو إمكانية ديال إضافة tweet ويزيد تعليقات على tweets.
الكود ديال الملف هو :
import React from "react";
import { useDispatch, connect } from "react-redux";
import { Link } from "react-router-dom";
import { isLogged } from "../helpers/auth";
import { getAllPosts } from "../redux/actions/postActions";
import PostList from "../components/PostList";
import AddPost from "../components/AddPost";
function Home({ posts }) {
const jwt = isLogged();
const dispatch = useDispatch();
React.useEffect(() => {
if (jwt) {
function loadPosts() {
dispatch(getAllPosts(jwt.token, jwt.user._id));
}
loadPosts();
}
}, []);
if (!jwt) {
return (
<div className="container">
<div className="row my-5">
<div className="col-md-8 mx-auto">
<div className="alert alert-info">
<Link to="/login">Connectez vous pour voir les tweets</Link>
</div>
</div>
</div>
</div>
);
}
return (
<div className="container">
<AddPost />
<PostList posts={posts && posts} />
</div>
);
}
const mapStateToProps = ({ post: { posts } }) => ({
posts,
});
export default connect(mapStateToProps, null)(Home);
5- إضافة ل component PostList
ف dossier components زيد fichier PostList.js لي فيه غادي يتعرضوا les tweets لي كنرسلوهم من ل component Home.
الكود ديال الملف هو :
import React from "react";
import Post from "./Post";
export default function PostList({ posts }) {
const [data, setData] = React.useState([]);
React.useEffect(() => {
setData(posts);
}, [posts]);
return (
<div className="row my-5">
<div className="col-md-8 mx-auto">
{data &&
data.map((item, i) => {
return <Post post={item} key={item._id} />;
})}
</div>
</div>
);
}