├── .gitignore
├── README.md
├── netlify.toml
├── package-lock.json
├── package.json
├── public
├── index.html
├── logo.png
├── manifest.json
└── robots.txt
└── src
├── App.js
├── Data
└── Home.js
├── assets
├── friends.png
├── groups.png
├── messenger.png
├── no_feed.svg
└── selectFriends.svg
├── components
├── Auth
│ ├── LoginForm.js
│ ├── RecentAccount
│ │ ├── AccountCard.js
│ │ ├── AddAccount.js
│ │ ├── LoginCard.js
│ │ └── RecentAccounts.js
│ ├── SignupForm.js
│ └── hooks
│ │ ├── useLoginUser.js
│ │ └── useSignupUser.js
├── Chat
│ ├── FriendNotSelected.js
│ ├── Friends.js
│ ├── MessageTextArea.js
│ └── Messages.js
├── Comment
│ ├── Comment.js
│ └── CommentTextArea.js
├── Friends
│ ├── Friend.js
│ ├── MyFriendLists.js
│ ├── SearchFriends.js
│ ├── UserLists.js
│ └── styles.js
├── InputField.js
├── Loader.js
├── Navbar
│ ├── BottomNav.js
│ ├── CreatePostMenu.js
│ ├── DrawerBar.js
│ ├── MiddleMenu.js
│ ├── Navbar.js
│ ├── ProfileMenu.js
│ ├── RightMenu.js
│ └── styles.js
├── NotificationMenu.js
├── PeopleYouMayKnow.js
├── Post
│ ├── LikePost.js
│ ├── Post.js
│ ├── PostContent.js
│ ├── PostFooter.js
│ ├── PostForm
│ │ ├── PostDialog
│ │ │ ├── CameraField.js
│ │ │ ├── CustomHeaderText.js
│ │ │ ├── DialogHeader.js
│ │ │ ├── FeelingsCard.js
│ │ │ ├── FileField.js
│ │ │ ├── LocationField.js
│ │ │ ├── PostFormCard.js
│ │ │ ├── PreviewImage.js
│ │ │ └── TagUserCard.js
│ │ └── WritePostCard.js
│ ├── PostSubContent.js
│ ├── Posts.js
│ └── hooks
│ │ └── useCreatePost.js
├── Profile
│ ├── Friends.js
│ ├── Photos.js
│ ├── PopoverProfile.js
│ ├── PopoverProfileCard.js
│ ├── ProfileHeader.js
│ ├── ProfileTimeline.js
│ ├── UpdateCoverImage.js
│ ├── UpdateProfileImage.js
│ └── UserProfile.js
├── Search.js
├── Sidebar.js
├── UI
│ ├── AvartarText.js
│ ├── DialogLoading.js
│ ├── GoogleMap.js
│ └── StyledBadge.js
└── settings
│ ├── Blocking.js
│ ├── General.js
│ ├── General
│ └── EditInput.js
│ ├── Location.js
│ ├── Location
│ └── EditLocation.js
│ ├── Privacy.js
│ ├── SecurityAndLogin.js
│ ├── SecurityAndLogin
│ └── EditPassword.js
│ └── TrigoInformation.js
├── context
├── ChatContext.js
├── PostContext.js
├── UIContext.js
└── UserContext.js
├── firebase
└── firebase.js
├── hooks
├── useCreateComment.js
├── useFetchComments.js
├── useFetchPost.js
├── useFriendActions.js
├── useLocationService.js
├── useSearchFriends.js
├── useSendMessage.js
├── useTheme.js
├── useUpdateProfile.js
└── useUpdateProfilePic.js
├── index.css
├── index.js
├── screens
├── Auth.js
├── Friends.js
├── Home.js
├── Messenger.js
├── Post.js
├── Profile.js
└── Settings.js
├── services
├── AuthService.js
├── ChatService.js
├── PostServices.js
└── UserServices.js
└── utils
├── FilterArray.js
└── ProtectedRoute.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.local
18 | .env.development
19 | .env.test.local
20 | .env.production
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # Local Netlify folder
27 | .netlify
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "CI= npm run build"
3 | publish = "build"
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "facebook-clone-app-react-client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@fortawesome/fontawesome-svg-core": "^1.2.30",
7 | "@fortawesome/free-brands-svg-icons": "^5.14.0",
8 | "@fortawesome/free-regular-svg-icons": "^5.14.0",
9 | "@fortawesome/free-solid-svg-icons": "^5.14.0",
10 | "@fortawesome/react-fontawesome": "^0.1.11",
11 | "@material-ui/core": "^4.11.0",
12 | "@material-ui/icons": "^4.9.1",
13 | "@material-ui/lab": "^4.0.0-alpha.56",
14 | "axios": "^0.20.0",
15 | "classnames": "^2.2.6",
16 | "dotenv": "^8.2.0",
17 | "emoji-picker-react": "^3.2.4",
18 | "firebase": "^7.22.0",
19 | "jwt-decode": "^3.0.0",
20 | "moment": "^2.29.1",
21 | "react": "^16.13.1",
22 | "react-dom": "^16.13.1",
23 | "react-eva-icons": "0.0.8",
24 | "react-hook-google-maps": "0.0.3",
25 | "react-router-dom": "^5.2.0",
26 | "react-scripts": "3.4.1",
27 | "socket.io-client": "^2.3.1",
28 | "words-to-numbers": "^1.5.1"
29 | },
30 | "scripts": {
31 | "start": "react-scripts start",
32 | "build": "react-scripts build",
33 | "test": "react-scripts test",
34 | "eject": "react-scripts eject"
35 | },
36 | "eslintConfig": {
37 | "extends": "react-app"
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.2%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 1 chrome version",
47 | "last 1 firefox version",
48 | "last 1 safari version"
49 | ]
50 | },
51 | "devDependencies": {}
52 | }
53 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Facebook Clone
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshmangalam/facebook-clone-app-react-client/4831ea375b710bf63477cc67ac2147b2c6e960a5/public/logo.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "logo.png",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/Data/Home.js:
--------------------------------------------------------------------------------
1 | export const homeLeftItems = [
2 | { title: 'Friends', img: 'friends.png', to: '/friends' },
3 | { title: 'Messenger', img: 'messenger.png', to: '/messenger' },
4 | ]
5 |
--------------------------------------------------------------------------------
/src/assets/friends.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshmangalam/facebook-clone-app-react-client/4831ea375b710bf63477cc67ac2147b2c6e960a5/src/assets/friends.png
--------------------------------------------------------------------------------
/src/assets/groups.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshmangalam/facebook-clone-app-react-client/4831ea375b710bf63477cc67ac2147b2c6e960a5/src/assets/groups.png
--------------------------------------------------------------------------------
/src/assets/messenger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshmangalam/facebook-clone-app-react-client/4831ea375b710bf63477cc67ac2147b2c6e960a5/src/assets/messenger.png
--------------------------------------------------------------------------------
/src/assets/no_feed.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/assets/selectFriends.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/src/components/Auth/LoginForm.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from 'react'
2 |
3 | import {
4 | Button,
5 | CircularProgress,
6 | FormControl,
7 | TextField,
8 | } from '@material-ui/core'
9 |
10 | import useLoginUser from './hooks/useLoginUser'
11 |
12 | function LoginForm() {
13 | const {
14 | loading,
15 | error,
16 | handleLoginUser,
17 | handlePasswordChange,
18 | handleEmailChange,
19 | } = useLoginUser()
20 |
21 | return (
22 |
23 |
67 |
68 | )
69 | }
70 |
71 | export default LoginForm
72 |
--------------------------------------------------------------------------------
/src/components/Auth/RecentAccount/AccountCard.js:
--------------------------------------------------------------------------------
1 | import {
2 | Badge,
3 | Card,
4 | CardActionArea,
5 | CardHeader,
6 | CardMedia,
7 | IconButton,
8 | Typography,
9 | } from '@material-ui/core'
10 | import { UserContext, UIContext } from '../../../App'
11 |
12 | import { Close, Notifications } from '@material-ui/icons'
13 | import React, { useContext, useState } from 'react'
14 | import AvartarText from '../../UI/AvartarText'
15 | import LoginCard from './LoginCard'
16 |
17 | function AccountCard({ account }) {
18 | const { userState, userDispatch } = useContext(UserContext)
19 | const { uiDispatch } = useContext(UIContext)
20 |
21 | const [loginOpen, setLoginOpen] = React.useState(false)
22 | const [userData, setUserData] = useState(null)
23 | const handleRemoveAccount = (account_id) => {
24 | userDispatch({ type: 'REMOVE_ACCOUNT', payload: account_id })
25 | uiDispatch({
26 | type: 'SET_MESSAGE',
27 | payload: {
28 | color: 'success',
29 | text: 'Account removed',
30 | display: true,
31 | },
32 | })
33 | }
34 |
35 | const handleClickOpen = (account_id) => {
36 | const user = userState.recentAccounts.find(
37 | (account) => account.id == account_id,
38 | )
39 |
40 | if (user) {
41 | setUserData(user)
42 | setLoginOpen(true)
43 | }
44 | }
45 | return (
46 | <>
47 |
48 | handleClickOpen(account.id)}>
49 | {account.profile_pic ? (
50 |
54 | ) : (
55 |
64 |
70 |
71 | )}
72 |
75 |
76 |
77 | }
78 | subheader={
79 |
80 | {account.name.slice(0, 6)}..
81 |
82 | }
83 | />
84 |
85 | handleRemoveAccount(account.id)}
87 | size="small"
88 | style={{
89 | position: 'absolute',
90 | background: 'tomato',
91 | color: '#fff',
92 | top: 0,
93 | right: 0,
94 | }}
95 | >
96 |
97 |
98 |
99 |
100 | {loginOpen && (
101 |
107 | )}
108 | >
109 | )
110 | }
111 |
112 | export default AccountCard
113 |
--------------------------------------------------------------------------------
/src/components/Auth/RecentAccount/AddAccount.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Card,
4 | CardActionArea,
5 | CardContent,
6 | CardHeader,
7 | CardMedia,
8 | Dialog,
9 | IconButton,
10 | Typography,
11 | } from '@material-ui/core'
12 | import { Add, Close } from '@material-ui/icons'
13 | import React, { useContext } from 'react'
14 | import { UIContext } from '../../../App'
15 | import LoginForm from '../LoginForm'
16 |
17 | function AddAccount() {
18 | const [addAccount, handleAddAccount] = React.useState(false)
19 | const {uiState} = useContext(UIContext)
20 | return (
21 | <>
22 |
23 | handleAddAccount(true)}>
24 |
33 |
41 |
42 |
43 |
44 |
45 | Add Account
46 |
47 |
48 |
49 | {addAccount && (
50 |
82 | )}
83 |
84 |
85 | >
86 | )
87 | }
88 |
89 | export default AddAccount
90 |
--------------------------------------------------------------------------------
/src/components/Auth/RecentAccount/LoginCard.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Button,
4 | Card,
5 | CardContent,
6 | CardHeader,
7 | CardMedia,
8 | CircularProgress,
9 | Dialog,
10 | FormControl,
11 | IconButton,
12 | TextField,
13 | Typography,
14 | } from '@material-ui/core'
15 | import { Close } from '@material-ui/icons'
16 | import React, { useState } from 'react'
17 | import AvartarText from '../../UI/AvartarText'
18 | import useLoginUser from '../hooks/useLoginUser'
19 |
20 | function LoginCard({ account, loginOpen, setLoginOpen, userData }) {
21 | const {
22 | loading,
23 | error,
24 | handleLoginUser,
25 | handlePasswordChange,
26 | } = useLoginUser(userData)
27 |
28 | return (
29 |
137 | )
138 | }
139 |
140 | export default LoginCard
141 |
--------------------------------------------------------------------------------
/src/components/Auth/RecentAccount/RecentAccounts.js:
--------------------------------------------------------------------------------
1 | import { Grid } from '@material-ui/core'
2 |
3 | import { UserContext } from '../../../App'
4 |
5 | import React, { useContext } from 'react'
6 |
7 | import AccountCard from './AccountCard'
8 | import AddAccount from './AddAccount'
9 |
10 | function RecentAccounts() {
11 | const { userState } = useContext(UserContext)
12 |
13 | return (
14 | <>
15 |
16 | {userState.recentAccounts.map((account) => (
17 |
18 |
19 |
20 | ))}
21 |
22 |
23 |
24 |
25 | >
26 | )
27 | }
28 |
29 | export default RecentAccounts
30 |
--------------------------------------------------------------------------------
/src/components/Auth/SignupForm.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from 'react'
2 |
3 | import {
4 | Button,
5 | CircularProgress,
6 | FormControl,
7 | TextField,
8 | } from '@material-ui/core'
9 |
10 | import useSignupUser from './hooks/useSignupUser'
11 |
12 | function LoginForm() {
13 | const {
14 | loading,
15 | error,
16 | handleSignupUser,
17 | handleNameChange,
18 | handlePasswordChange,
19 | handleEmailChange,
20 | } = useSignupUser()
21 |
22 | return (
23 |
24 |
80 |
81 | )
82 | }
83 |
84 | export default LoginForm
85 |
--------------------------------------------------------------------------------
/src/components/Auth/hooks/useLoginUser.js:
--------------------------------------------------------------------------------
1 | import { useState, useContext, useEffect } from 'react'
2 | import axios from 'axios'
3 | import { UserContext, UIContext } from '../../../App'
4 | import { useHistory } from 'react-router-dom'
5 | import { fetchCurrentUser } from '../../../services/AuthService'
6 |
7 | const url = process.env.REACT_APP_ENDPOINT
8 |
9 | const useLoginUser = (userData = null) => {
10 | const { uiDispatch } = useContext(UIContext)
11 | const { userDispatch } = useContext(UserContext)
12 |
13 | const history = useHistory()
14 |
15 | const [loading, setLoading] = useState(false)
16 | const [error, setError] = useState(null)
17 | const [initialState, setInitialState] = useState({
18 | email: '',
19 | password: '',
20 | })
21 |
22 | useEffect(() => {
23 |
24 | setInitialState({ ...initialState, email: userData ? userData.email : '' })
25 | return () => {
26 |
27 | }
28 | }, [])
29 |
30 | const handlePasswordChange = (e) => {
31 | setInitialState({ ...initialState, password: e.target.value })
32 | setError({ ...error, password: '' })
33 | }
34 |
35 | const handleEmailChange = (e) => {
36 | setInitialState({ ...initialState, email: e.target.value })
37 | setError({ ...error, email: '' })
38 | }
39 |
40 | async function handleLoginUser(e) {
41 | e.preventDefault()
42 |
43 | setLoading(true)
44 | try {
45 | const { data } = await axios.post(`${url}/api/auth/login`, initialState)
46 | localStorage.setItem('token', JSON.stringify(data.data.token))
47 | const me = await fetchCurrentUser()
48 | setLoading(false)
49 |
50 | userDispatch({ type: 'SET_CURRENT_USER', payload: me.data.user })
51 |
52 | uiDispatch({
53 | type: 'SET_MESSAGE',
54 | payload: { color: 'success', display: true, text: data.message },
55 | })
56 |
57 |
58 | history.push('/home')
59 | } catch (err) {
60 | setLoading(false)
61 |
62 | console.log(err)
63 | if (err && err.response) {
64 | if (err.response.status === 422) {
65 | setError({ ...err.response.data.error })
66 | }
67 |
68 | if (err.response.status === 400) {
69 | uiDispatch({
70 | type: 'SET_MESSAGE',
71 | payload: {
72 | color: 'error',
73 | display: true,
74 | text: err.response.data.error,
75 | },
76 | })
77 | }
78 | }
79 | }
80 | }
81 |
82 | return {
83 | loading,
84 | error,
85 | handleLoginUser,
86 | handleEmailChange,
87 | handlePasswordChange,
88 | }
89 | }
90 |
91 | export default useLoginUser
92 |
--------------------------------------------------------------------------------
/src/components/Auth/hooks/useSignupUser.js:
--------------------------------------------------------------------------------
1 | import { useState, useContext } from 'react'
2 | import axios from 'axios'
3 | import { UserContext, UIContext } from '../../../App'
4 | import { useHistory } from 'react-router-dom'
5 | import { fetchCurrentUser } from '../../../services/AuthService'
6 |
7 | const url = process.env.REACT_APP_ENDPOINT
8 |
9 | const useSignupUser = () => {
10 | const { uiDispatch } = useContext(UIContext)
11 | const { userDispatch } = useContext(UserContext)
12 |
13 | const history = useHistory()
14 |
15 | const [loading, setLoading] = useState(false)
16 | const [error, setError] = useState(null)
17 | const [initialState, setInitialState] = useState({
18 | name: '',
19 | email: '',
20 | password: '',
21 | })
22 | const handleNameChange = (e) => {
23 | setError({ ...error, name: '' })
24 | setInitialState({ ...initialState, name: e.target.value })
25 | }
26 |
27 | const handleEmailChange = (e) => {
28 | setError({ ...error, email: '' })
29 | setInitialState({ ...initialState, email: e.target.value })
30 | }
31 |
32 | const handlePasswordChange = (e) => {
33 | setInitialState({ ...initialState, password: e.target.value })
34 | setError({ ...error, password: '' })
35 | }
36 |
37 | async function handleSignupUser(e) {
38 | e.preventDefault()
39 |
40 | setLoading(true)
41 | try {
42 | const { data } = await axios.post(`${url}/api/auth/signup`, initialState)
43 | localStorage.setItem('token', JSON.stringify(data.data.token))
44 | const me = await fetchCurrentUser()
45 | setLoading(false)
46 |
47 | userDispatch({ type: 'SET_CURRENT_USER', payload: me.data.user })
48 |
49 | uiDispatch({
50 | type: 'SET_MESSAGE',
51 | payload: { color: 'success', display: true, text: data.message },
52 | })
53 |
54 |
55 | history.push('/home')
56 | } catch (err) {
57 | setLoading(false)
58 |
59 | console.log(err)
60 | if (err && err.response) {
61 | if (err.response.status === 422) {
62 | setError({ ...err.response.data.error })
63 | }
64 |
65 | if (err.response.status === 400) {
66 | uiDispatch({
67 | type: 'SET_MESSAGE',
68 | payload: {
69 | color: 'error',
70 | display: true,
71 | text: err.response.data.error,
72 | },
73 | })
74 | }
75 | }
76 | }
77 | }
78 |
79 | return {
80 | loading,
81 | error,
82 | handleSignupUser,
83 | handleNameChange,
84 | handleEmailChange,
85 | handlePasswordChange,
86 | }
87 | }
88 |
89 | export default useSignupUser
90 |
--------------------------------------------------------------------------------
/src/components/Chat/FriendNotSelected.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Grid, Paper, Typography } from '@material-ui/core'
2 | import React from 'react'
3 |
4 | function FriendNotSelected() {
5 | return (
6 |
20 |
21 |
25 |
26 |
27 |
28 | Select friends from friend lists to start chat
29 |
30 |
31 | )
32 | }
33 |
34 | export default FriendNotSelected
35 |
--------------------------------------------------------------------------------
/src/components/Chat/Friends.js:
--------------------------------------------------------------------------------
1 | import {
2 | List,
3 | ListItem,
4 | ListItemAvatar,
5 | ListItemText,
6 | Avatar,
7 | ListSubheader,
8 | Typography,
9 | } from '@material-ui/core'
10 | import React, { useContext } from 'react'
11 | import { UserContext, ChatContext, UIContext } from '../../App'
12 | import { fetchFriendMessages } from '../../services/ChatService'
13 | import AvartarText from '../UI/AvartarText'
14 |
15 | function Friends() {
16 | const { userState } = useContext(UserContext)
17 | const { chatDispatch } = useContext(ChatContext)
18 | const { uiState, uiDispatch } = useContext(UIContext)
19 |
20 | const handleFriendSelect = (friend) => {
21 | uiDispatch({ type: 'SET_DRAWER', payload: false })
22 | chatDispatch({ type: 'SET_SELECTED_FRIEND', payload: friend })
23 | fetchFriendMessages(friend.id)
24 | .then((res) => {
25 | if (res.data) {
26 | chatDispatch({ type: 'SET_MESSAGES', payload: res.data.data })
27 | }
28 | })
29 | .catch((err) => console.log(err))
30 | }
31 | return (
32 | Your Friends}
34 | style={{ backgroundColor: uiState.darkMode && 'rgb(36,37,38)' }}
35 | >
36 | {userState.currentUser.friends && userState.currentUser.friends.length
37 | ? userState.currentUser.friends.map((friend) => {
38 | return (
39 | handleFriendSelect(friend)}
43 | >
44 |
45 | {friend.profile_pic ? (
46 |
47 | ) : (
48 |
52 | )}
53 |
54 |
55 |
56 | )
57 | })
58 | : No Friends}
59 |
60 | )
61 | }
62 |
63 | export default Friends
64 |
--------------------------------------------------------------------------------
/src/components/Chat/MessageTextArea.js:
--------------------------------------------------------------------------------
1 | import { IconButton, InputBase, Paper, makeStyles } from '@material-ui/core'
2 | import { Send } from '@material-ui/icons'
3 | import React, { useContext, useState } from 'react'
4 | import { UIContext } from '../../App'
5 | import useSendMessage from '../../hooks/useSendMessage'
6 |
7 | const useStyles = makeStyles((theme) => ({
8 | inputRoot: {
9 | padding: '16px 8px 16px 8px',
10 | },
11 | inputInput: {
12 | flexGrow: 1,
13 | paddingLeft: '4px',
14 | },
15 | }))
16 |
17 | function MessageTextArea() {
18 | const { uiState } = useContext(UIContext)
19 | const classes = useStyles()
20 | const [textMessage, setTextMessage] = useState('')
21 |
22 | const { sendMessage } = useSendMessage({ textMessage, setTextMessage })
23 |
24 | const handleSendMessage = (e) => {
25 | e.preventDefault()
26 | sendMessage()
27 | }
28 | return (
29 |
39 | setTextMessage(e.target.value)}
42 | placeholder="Enter Your Text..."
43 | multiline
44 | rowsMax={4}
45 | classes={{
46 | root: classes.inputRoot,
47 | input: classes.inputInput,
48 | }}
49 | style={{
50 | borderRadius: '20px 20px 20px 20px',
51 | backgroundColor: uiState.darkMode ? 'rgb(24,25,26)' : 'whitesmoke',
52 | width: '100%',
53 | }}
54 | />
55 |
63 |
64 |
65 |
66 | )
67 | }
68 |
69 | export default MessageTextArea
70 |
--------------------------------------------------------------------------------
/src/components/Chat/Messages.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Grid, makeStyles, Paper, Typography } from '@material-ui/core'
2 | import React, { Fragment, useContext, useEffect, useRef } from 'react'
3 | import { ChatContext, UIContext, UserContext } from '../../App'
4 | import moment from 'moment'
5 | const useStyles = makeStyles((theme) => ({
6 | me: {
7 | padding: '8px',
8 | maxWidth: '60%',
9 | float: 'right',
10 | marginTop: '16px',
11 | },
12 |
13 | partner: {
14 | padding: '8px',
15 | maxWidth: '60%',
16 |
17 | margin: 'auto',
18 | float: 'left',
19 | padding: '8px',
20 | marginTop: '16px',
21 | },
22 | date: {
23 | fontSize: '12px',
24 |
25 | display: 'flex',
26 | alignItems: 'center',
27 | justifyContent: 'flex-end',
28 | padding: '8px',
29 | margin: 'auto',
30 | },
31 | }))
32 | function Messages() {
33 | const classes = useStyles()
34 |
35 | const scrollDiv = useRef(null)
36 |
37 | const { chatState } = useContext(ChatContext)
38 | const { userState } = useContext(UserContext)
39 | const { uiState } = useContext(UIContext)
40 |
41 | useEffect(() => {
42 | scrollToBottom()
43 | }, [chatState.messages.length])
44 |
45 | function scrollToBottom() {
46 | scrollDiv.current.scrollIntoView({ behavior: 'smooth' })
47 | }
48 | return (
49 |
50 | {chatState.messages.length
51 | ? chatState.messages.map((message) => (
52 |
53 | {userState.currentUser.id !== message.sender.id ? (
54 |
55 |
64 | {message.body.text && (
65 |
66 | {message.body.text}
67 |
68 | )}
69 | {message.body.image && (
70 |
71 |
76 |
77 | )}
78 |
79 | {moment(message.createdAt).fromNow()}
80 |
81 |
82 |
83 | ) : (
84 |
91 |
100 | {message.body.text && (
101 |
102 | {message.body.text}
103 |
104 | )}
105 | {message.body.image && (
106 |
107 |
112 |
113 | )}
114 |
118 | {moment(message.createdAt).fromNow()}
119 |
120 |
121 |
122 | )}
123 |
124 | ))
125 | : null}
126 |
127 |
128 | )
129 | }
130 |
131 | export default Messages
132 |
--------------------------------------------------------------------------------
/src/components/Comment/Comment.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Button,
4 | Divider,
5 | Paper,
6 | Typography,
7 | List,
8 | ListItem,
9 | ListItemText,
10 | ListItemAvatar,
11 | } from '@material-ui/core'
12 | import React, { useContext } from 'react'
13 | import AvartarText from '../UI/AvartarText'
14 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
15 | import { faThumbsUp as filledLike } from '@fortawesome/free-solid-svg-icons'
16 | import { faThumbsUp } from '@fortawesome/free-regular-svg-icons'
17 | import { likeDislikeComment } from '../../services/PostServices'
18 | import { PostContext, UserContext, UIContext } from '../../App'
19 | function Comment({ comment }) {
20 | const { postDispatch } = useContext(PostContext)
21 | const { userState } = useContext(UserContext)
22 | const { uiDispatch, uiState } = useContext(UIContext)
23 |
24 | function handleLikeComment() {
25 | likeDislikeComment(comment.id).then((res) => {
26 | if (res.data) {
27 | postDispatch({ type: 'LIKE_UNLIKE_COMMENT', payload: res.data.comment })
28 | uiDispatch({
29 | type: 'SET_MESSAGE',
30 | payload: { color: 'success', text: res.data.message, display: true },
31 | })
32 | }
33 | })
34 | }
35 |
36 | function isLiked() {
37 | return comment.likes.includes(userState.currentUser.id)
38 | }
39 |
40 | const listItems = (
41 |
42 |
43 | {comment.user.profile_pic ? (
44 |
45 |
50 |
51 | ) : (
52 |
56 | )}
57 |
58 |
61 | {comment.user.name}
62 |
63 | }
64 | secondary={
65 | <>
66 | {comment.body.text && comment.body.text}
67 |
68 | {comment.body.image && (
69 |
70 |
74 |
79 |
80 |
81 | )}
82 | >
83 | }
84 | />
85 |
86 | )
87 | return (
88 |
91 |
92 | {listItems}
93 |
94 |
101 |
108 | ) : (
109 |
110 | )
111 | }
112 | >
113 | ({comment.likes.length})
114 |
115 |
116 |
117 |
118 |
119 | )
120 | }
121 |
122 | export default Comment
123 |
--------------------------------------------------------------------------------
/src/components/Comment/CommentTextArea.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Grid,
4 | IconButton,
5 | LinearProgress,
6 | Paper,
7 | TextField,
8 | } from '@material-ui/core'
9 | import React, { useContext, useState } from 'react'
10 | import AvartarText from '../UI/AvartarText'
11 | import StyledBadge from '../UI/StyledBadge'
12 | import { UIContext, UserContext } from '../../App'
13 | import { SendOutlined } from '@material-ui/icons'
14 | import useCreateComment from '../../hooks/useCreateComment'
15 | function CommentTextArea({ post }) {
16 | const { userState } = useContext(UserContext)
17 | const { uiState } = useContext(UIContext)
18 |
19 | const [commentText, setCommentText] = useState('')
20 | const [commentImage, setCommentImage] = useState()
21 | const [error, setError] = useState('')
22 |
23 | const { createComment, loading } = useCreateComment({
24 | post_id: post.id,
25 | commentText,
26 | setCommentText,
27 | setCommentImage,
28 | commentImage,
29 | setError,
30 | })
31 |
32 | const handleCommentChange = (e) => {
33 | setError('')
34 | setCommentText(e.target.value)
35 | }
36 |
37 | return (
38 | <>
39 |
48 |
49 |
50 | {userState.currentUser.profile_pic ? (
51 |
52 |
57 |
58 | ) : (
59 |
60 | )}
61 |
62 |
63 |
64 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | {loading ? (
90 |
100 |
101 |
102 | ) : null}
103 | >
104 | )
105 | }
106 |
107 | export default CommentTextArea
108 |
--------------------------------------------------------------------------------
/src/components/Friends/Friend.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from 'react'
2 | import {
3 | Avatar,
4 | Typography,
5 | makeStyles,
6 | Card,
7 | CardActionArea,
8 | CardContent,
9 | } from '@material-ui/core'
10 | import { UIContext, UserContext } from '../../App'
11 | import AvartarText from '../UI/AvartarText'
12 |
13 | const useStyles = makeStyles((theme) => ({
14 | container: {
15 | paddingLeft: '6px',
16 | paddingTop: '6px',
17 | paddingBottom: '8px',
18 | '&:hover': { background: 'whitesmoke', cursor: 'pointer' },
19 | },
20 | }))
21 | function Friend({ user, children }) {
22 | const { userDispatch } = useContext(UserContext)
23 | const { uiState, uiDispatch } = useContext(UIContext)
24 | const classes = useStyles()
25 |
26 | return (
27 |
28 |
35 | {
37 | userDispatch({ type: 'ADD_SELECTED_USER_PROFILE', payload: user })
38 | uiDispatch({ type: 'SET_DRAWER', payload: false })
39 | }}
40 | >
41 |
48 | {user.profile_pic ? (
49 |
50 |
54 |
55 | ) : (
56 |
60 | )}
61 |
62 |
69 |
73 | {user.name}
74 |
75 | {7} mutual friends
76 |
77 |
78 |
79 |
80 | {children}
81 |
82 |
83 | )
84 | }
85 |
86 | export default Friend
87 |
--------------------------------------------------------------------------------
/src/components/Friends/MyFriendLists.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Grid,
4 | List,
5 | ListItem,
6 | ListItemAvatar,
7 | ListItemText,
8 | ListSubheader,
9 | Typography,
10 | } from '@material-ui/core'
11 | import React, { useContext } from 'react'
12 | import { UIContext, UserContext } from '../../App'
13 | import AvartarText from '../UI/AvartarText'
14 | import StyledBadge from '../UI/StyledBadge'
15 | import PopoverProfile from '../Profile/PopoverProfile'
16 | import { Link } from 'react-router-dom'
17 | function MyFriendLists() {
18 | const { userState } = useContext(UserContext)
19 | const { uiState } = useContext(UIContext)
20 | const friendsItem = (user) => (
21 |
22 |
23 |
24 | {user.profile_pic ? (
25 |
26 | ) : (
27 |
31 | )}
32 |
33 |
34 |
35 |
36 |
37 | )
38 | return (
39 | <>
40 | {userState.currentUser.friends && userState.currentUser.friends.length ? (
41 |
45 |
46 |
52 | Contacts
53 |
54 |
55 |
56 | }
57 | >
58 | {userState.currentUser.friends.map((user) => (
59 |
60 | {uiState.mdScreen ? (
61 |
{friendsItem(user)}
62 | ) : (
63 | friendsItem(user)
64 | )}
65 |
66 | ))}
67 |
68 | ) : null}
69 | >
70 | )
71 | }
72 |
73 | export default MyFriendLists
74 |
--------------------------------------------------------------------------------
/src/components/Friends/SearchFriends.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import {
3 | Avatar,
4 | Button,
5 | Dialog,
6 | DialogActions,
7 | DialogContent,
8 | IconButton,
9 | List,
10 | ListItemIcon,
11 | ListItemText,
12 | TextField,
13 | Typography,
14 | ListItem,
15 | CircularProgress,
16 | } from '@material-ui/core'
17 | import { Link } from 'react-router-dom'
18 | import useSearchFriends from '../../hooks/useSearchFriends'
19 | import { Search } from '@material-ui/icons'
20 | import AvartarText from '../UI/AvartarText'
21 |
22 | function SearchFriends() {
23 | const [open, setOpen] = useState(null)
24 | const [name, setName] = useState('')
25 | const { searchFriends, friends, loading } = useSearchFriends()
26 |
27 | const handleSearch = () => {
28 | searchFriends(name)
29 | }
30 | const handleClose = () => {
31 | setOpen(false)
32 | }
33 |
34 | const handleOpen = () => {
35 | setOpen(true)
36 | }
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
126 |
127 | )
128 | }
129 |
130 | export default SearchFriends
131 |
--------------------------------------------------------------------------------
/src/components/Friends/UserLists.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import Friend from './Friend'
3 | import { Button, CardActions, Grid, Typography } from '@material-ui/core'
4 | import { UserContext } from '../../App'
5 | import useFriendActions from '../../hooks/useFriendActions'
6 | function UserLists({ users }) {
7 | const { userState } = useContext(UserContext)
8 |
9 | const { sendFriendRequest } = useFriendActions()
10 |
11 | const handleSendFriendRequest = (user_id) => {
12 | sendFriendRequest(user_id)
13 | }
14 |
15 | const filterUser = (user) => {
16 | let s_index = userState.sendedFriendRequests.findIndex(
17 | (request) => request.user.id == user.id,
18 | )
19 | let r_index = userState.receivedFriendRequests.findIndex(
20 | (request) => request.user.id == user.id,
21 | )
22 | let already_friend = userState.currentUser.friends.findIndex(
23 | (friend) => friend.id == user.id,
24 | )
25 | let currentUser = userState.currentUser.id == user.id
26 |
27 | if (
28 | s_index === -1 &&
29 | r_index === -1 &&
30 | already_friend === -1 &&
31 | !currentUser
32 | ) {
33 | return true
34 | }
35 | return false
36 | }
37 |
38 | return (
39 |
49 |
57 | People You May Know
58 |
59 | {users && users.length
60 | ? users.map((user) => (
61 |
62 | {filterUser(user) && (
63 |
64 |
65 |
75 |
84 |
85 |
86 | )}
87 |
88 | ))
89 | : null}
90 |
91 | )
92 | }
93 |
94 | export default UserLists
95 |
--------------------------------------------------------------------------------
/src/components/Friends/styles.js:
--------------------------------------------------------------------------------
1 | const { makeStyles } = require("@material-ui/core");
2 |
3 | const drawerWidth = 380;
4 | export default makeStyles((theme) => ({
5 | drawer: {
6 | width: drawerWidth,
7 | flexShrink: 0,
8 | },
9 | drawerPaper: {
10 | width: drawerWidth,
11 | backgroundColor: "white",
12 | boxShadow:'1px 1px 3px rgba(0,0,0,0.1)'
13 | },
14 | drawerContainer: {
15 | overflow: "auto",
16 | },
17 | }));
18 |
--------------------------------------------------------------------------------
/src/components/InputField.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles, InputBase } from '@material-ui/core'
3 | import { Search as SearchIcon } from '@material-ui/icons'
4 | const useStyles = makeStyles((theme) => ({
5 | search: {
6 | position: 'relative',
7 | borderRadius: '50px 50px 50px 50px',
8 | backgroundColor: '#F0F2F5',
9 | width:'100%',
10 | marginLeft:'8px'
11 | },
12 |
13 | searchIcon: {
14 | color: '#606770;',
15 | padding: theme.spacing(0, 2),
16 | height: '100%',
17 | position: 'absolute',
18 | display: 'flex',
19 | alignItems: 'center',
20 | justifyContent: 'center',
21 | right:0,
22 | marginLeft:'16px',
23 | cursor:"pointer"
24 | },
25 | inputRoot: {
26 | color: 'black',
27 | width:'70%',
28 |
29 | },
30 | inputInput: {
31 | flexGrow: 1,
32 | paddingLeft: '8px',
33 | width:'100%'
34 |
35 |
36 | },
37 | }))
38 | function Search({ placeholder,children }) {
39 | const classes = useStyles()
40 | return (
41 |
42 |
43 | {children}
44 |
45 |
53 |
54 | )
55 | }
56 |
57 | export default Search
58 |
--------------------------------------------------------------------------------
/src/components/Loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Paper, CircularProgress, Backdrop, LinearProgress } from "@material-ui/core";
3 | function Loader() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
17 | export default Loader
18 |
--------------------------------------------------------------------------------
/src/components/Navbar/BottomNav.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { BottomNavigation, Paper } from '@material-ui/core'
3 | import { UIContext } from "../../App";
4 | import MiddleMenu from './MiddleMenu'
5 |
6 | function BottomNav() {
7 | const {uiState} = useContext(UIContext)
8 | return (
9 |
13 |
23 |
24 |
25 |
26 | )
27 | }
28 |
29 | export default BottomNav
30 |
--------------------------------------------------------------------------------
/src/components/Navbar/CreatePostMenu.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react'
2 | import { UIContext } from '../../App'
3 | import { useHistory } from 'react-router-dom'
4 | import {
5 | Menu,
6 | IconButton,
7 | List,
8 | ListItem,
9 | ListItemIcon,
10 | ListItemText,
11 | ListSubheader,
12 | Typography,
13 | Avatar,
14 | useTheme,
15 | useMediaQuery,
16 | } from '@material-ui/core'
17 | import {
18 | Add as AddIcon,
19 | PostAdd as PostIcon,
20 | } from '@material-ui/icons'
21 |
22 | function CreatePostMenu() {
23 | const history = useHistory()
24 | const { uiState, uiDispatch } = useContext(UIContext)
25 |
26 | const [postMenu, setPostMenu] = useState(null)
27 | const theme = useTheme()
28 | const xsScreen = useMediaQuery(theme.breakpoints.only('xs'))
29 |
30 | const handlePostOpen = () => {
31 | history.push('/')
32 | uiDispatch({ type: 'SET_POST_MODEL', payload: true })
33 | setPostMenu(null)
34 | }
35 | return (
36 |
37 |
setPostMenu(e.currentTarget)}
44 | >
45 |
48 |
49 |
50 |
95 |
96 | )
97 | }
98 |
99 | export default CreatePostMenu
100 |
--------------------------------------------------------------------------------
/src/components/Navbar/DrawerBar.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { UIContext } from '../../App'
3 | import { makeStyles } from '@material-ui/core/styles'
4 | import Drawer from '@material-ui/core/Drawer'
5 | import Toolbar from '@material-ui/core/Toolbar'
6 | import { Button } from '@material-ui/core'
7 |
8 | const drawerWidth = '100vw'
9 |
10 | const useStyles = makeStyles((theme) => ({
11 | root: {
12 | display: 'flex',
13 | },
14 |
15 | drawer: {
16 | width: drawerWidth,
17 | flexShrink: 0,
18 | },
19 | drawerPaper: (darkMode) => ({
20 | width: drawerWidth,
21 | backgroundColor: darkMode && 'rgb(36,37,38)',
22 | }),
23 | }))
24 |
25 | export default function DrawerBar({ children }) {
26 | const { uiState, uiDispatch } = useContext(UIContext)
27 | const classes = useStyles(uiState.darkMode)
28 |
29 | return (
30 |
31 |
uiDispatch({ type: 'SET_DRAWER', payload: false })}
39 | >
40 |
41 | {children}
42 |
51 |
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/Navbar/MiddleMenu.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react'
2 | import { NavLink, useLocation } from 'react-router-dom'
3 | import { Button } from '@material-ui/core'
4 |
5 | import useStyles from './styles'
6 |
7 | import { Home, HomeOutlined, Person, PersonOutlined } from '@material-ui/icons'
8 |
9 | function MiddleMenu() {
10 | const classes = useStyles()
11 | const location = useLocation()
12 |
13 | return (
14 |
15 |
32 |
44 |
45 | )
46 | }
47 |
48 | export default MiddleMenu
49 |
--------------------------------------------------------------------------------
/src/components/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from 'react'
2 | import { UIContext, UserContext } from '../../App'
3 | import useStyles from './styles'
4 | import MiddleMenu from './MiddleMenu'
5 | import RightMenu from './RightMenu'
6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
7 | import { faFacebook } from '@fortawesome/free-brands-svg-icons'
8 | import {
9 | AppBar,
10 | Toolbar,
11 | IconButton,
12 | useMediaQuery,
13 | useTheme,
14 | Tooltip,
15 | } from '@material-ui/core'
16 | import {Menu as MenuIcon } from '@material-ui/icons'
17 | import SearchFriends from '../Friends/SearchFriends'
18 | function Navbar() {
19 | const { uiState, uiDispatch } = useContext(UIContext)
20 | const classes = useStyles()
21 | const theme = useTheme()
22 | const match = useMediaQuery(theme.breakpoints.between(960, 1400))
23 | const xsScreen = useMediaQuery(theme.breakpoints.only('xs'))
24 |
25 | return (
26 |
27 |
38 |
39 |
40 |
50 |
51 |
52 |
53 |
54 |
55 | {!uiState.mdScreen && uiState.navDrawerMenu && (
56 |
64 |
66 | uiDispatch({ type: 'SET_DRAWER', payload: !uiState.drawer })
67 | }
68 | >
69 |
70 |
71 |
72 | )}
73 |
74 |
75 | {uiState.mdScreen && (
76 |
77 |
78 |
79 | )}
80 |
81 |
82 |
83 |
84 |
85 |
86 | )
87 | }
88 |
89 | export default Navbar
90 |
--------------------------------------------------------------------------------
/src/components/Navbar/RightMenu.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from 'react'
2 | import { NavLink } from 'react-router-dom'
3 | import { UIContext, UserContext } from '../../App'
4 |
5 | import {
6 | Chip,
7 | Avatar,
8 | IconButton,
9 | Badge,
10 | useMediaQuery,
11 | useTheme,
12 | } from '@material-ui/core'
13 | import useStyles from './styles'
14 |
15 | import { faFacebookMessenger } from '@fortawesome/free-brands-svg-icons'
16 | import { faBell } from '@fortawesome/free-regular-svg-icons'
17 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
18 | import ProfileMenu from './ProfileMenu'
19 | import CreatePostMenu from './CreatePostMenu'
20 | import AvartarText from '../UI/AvartarText'
21 | import NotificationMenu from '../NotificationMenu'
22 | function RightMenu() {
23 | const classes = useStyles()
24 | const { uiState } = useContext(UIContext)
25 | const { userState } = useContext(UserContext)
26 | const theme = useTheme()
27 | const xsScreen = useMediaQuery(theme.breakpoints.only('xs'))
28 |
29 | const defaultPropsNotif = {
30 | color: 'error',
31 | children: ,
32 | }
33 |
34 | return (
35 |
36 | {uiState.mdScreen && (
37 |
43 | {userState.currentUser.name.split(' ')[0].slice(0, 5) + '..'}
44 |
45 | }
46 | className={classes.profile_chip}
47 | avatar={
48 | userState.currentUser.profile_pic ? (
49 |
50 | ) : (
51 |
55 | )
56 | }
57 | />
58 | )}
59 |
60 |
61 |
71 |
75 |
76 |
77 |
82 |
83 |
84 |
85 |
86 | )
87 | }
88 |
89 | export default RightMenu
90 |
--------------------------------------------------------------------------------
/src/components/Navbar/styles.js:
--------------------------------------------------------------------------------
1 | import { makeStyles, fade } from '@material-ui/core'
2 |
3 | const drawerWidth = 380
4 | export default makeStyles((theme) => ({
5 | root: {
6 | flexGrow: 1,
7 | zIndex: theme.zIndex.drawer + 1,
8 | },
9 |
10 | leftMenu: {
11 | flexGrow: 1,
12 | display: 'flex',
13 | flexDirection: 'row',
14 | alignItems: 'center',
15 | },
16 |
17 | logoImg: {
18 | width: theme.spacing(7),
19 | height: theme.spacing(7),
20 | },
21 |
22 | middleMenu: {
23 | flexGrow: 5,
24 | display: 'flex',
25 | flexDirection: 'row',
26 | alignItems: 'center',
27 | justifyContent: 'center',
28 | },
29 |
30 | buttonItemMiddle: {
31 | width: 'auto',
32 | height: 'auto',
33 | padding: '10px 50px 10px 50px',
34 | borderRadius: '10px 10px 10px 10px',
35 | },
36 |
37 | activeBtn: {
38 | borderBottom: '4px solid #3578E5',
39 | borderRadius: '0px',
40 | background: 'transparent',
41 | },
42 |
43 | rightMenu: {
44 | flexGrow: 2,
45 | display: 'flex',
46 | flexDirection: 'row',
47 | justifyContent: 'flex-end',
48 | alignItems: 'center',
49 | },
50 |
51 | profile_chip: {
52 | paddingTop: '16px',
53 | paddingBottom: '16px',
54 | '&:hover': {
55 | cursor: 'pointer',
56 | },
57 | },
58 | }))
59 |
--------------------------------------------------------------------------------
/src/components/NotificationMenu.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react'
2 | import { UIContext } from '../App'
3 | import moment from 'moment'
4 | import {
5 | Menu,
6 | IconButton,
7 | List,
8 | ListItem,
9 | ListItemIcon,
10 | ListItemText,
11 | ListSubheader,
12 | Typography,
13 | Avatar,
14 | useTheme,
15 | useMediaQuery,
16 | } from '@material-ui/core'
17 | import {
18 | DeleteOutlined,
19 | Notifications as NotificationIcon,
20 | } from '@material-ui/icons'
21 | import useUpdateProfile from '../hooks/useUpdateProfile'
22 |
23 | function NotificationMenu({ children }) {
24 | const { uiState } = useContext(UIContext)
25 | const [menu, setMenu] = useState(null)
26 | const theme = useTheme()
27 | const xsScreen = useMediaQuery(theme.breakpoints.only('xs'))
28 |
29 | const { clearNotification } = useUpdateProfile()
30 | return (
31 |
32 | setMenu(e.currentTarget)}
39 | >
40 | {children}
41 |
42 |
43 |
112 |
113 | )
114 | }
115 |
116 | export default NotificationMenu
117 |
--------------------------------------------------------------------------------
/src/components/PeopleYouMayKnow.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import {
3 | Paper,
4 | Avatar,
5 | Typography,
6 | Button,
7 | Grid,
8 | Card,
9 | CardMedia,
10 | CardContent,
11 | IconButton,
12 | } from '@material-ui/core'
13 | import { UIContext, UserContext } from '../App'
14 | import {
15 | MoreHoriz as MoreHorizIcon,
16 | PeopleAlt as PeopleAltIcon,
17 | } from '@material-ui/icons'
18 | import AvartarText from './UI/AvartarText'
19 | function PeopleYouMayKnow({ users }) {
20 | const { userState } = useContext(UserContext)
21 |
22 | return (
23 |
24 |
25 |
26 |
33 | People You may Know
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
49 | {users.map((user) => (
50 |
51 |
52 | {user.profile_pic && (
53 |
58 | )}
59 |
60 |
61 | {user.name}
62 |
63 | {user.friends && user.friends.length
64 | ? user.friends.map((friend) => (
65 |
66 | {friend.profile_pic ? (
67 |
72 | ) : (
73 |
78 | )}
79 |
80 |
86 | {user.friends.length} friends
87 |
88 |
89 | ))
90 | : null}
91 |
92 |
93 |
94 |
95 | ))}
96 |
97 |
98 | )
99 | }
100 |
101 | export default PeopleYouMayKnow
102 |
--------------------------------------------------------------------------------
/src/components/Post/LikePost.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from 'react'
2 | import { Button } from '@material-ui/core'
3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4 | import { faThumbsUp } from '@fortawesome/free-regular-svg-icons'
5 | import {faThumbsUp as filledLike} from "@fortawesome/free-solid-svg-icons";
6 |
7 | import { PostContext, UIContext, UserContext } from '../../App'
8 | import { likeDislikePost } from '../../services/PostServices'
9 |
10 |
11 | function LikePost({ post }) {
12 |
13 | const { postDispatch } = useContext(PostContext)
14 | const { uiDispatch } = useContext(UIContext)
15 | const { userState } = useContext(UserContext)
16 |
17 | const isLiked = () => {
18 | return post.likes.includes(userState.currentUser.id)
19 | }
20 | const handleLike = () => {
21 | likeDislikePost(post.id)
22 | .then((res) => {
23 | if (res.data) {
24 | postDispatch({
25 | type: 'LIKE_UNLIKE_POST',
26 | payload: res.data.post,
27 | })
28 | uiDispatch({
29 | type: 'SET_MESSAGE',
30 | payload: {
31 | color: 'success',
32 | text: res.data.message,
33 | display: true,
34 | },
35 | })
36 | }
37 | if (res.error) {
38 | uiDispatch({
39 | type: 'SET_MESSAGE',
40 | payload: {
41 | color: 'error',
42 | text: res.data.error,
43 | display: true,
44 | },
45 | })
46 | }
47 | })
48 | .catch((err) => console.log(err))
49 | }
50 |
51 | return (
52 |
53 |
60 | ) : (
61 |
62 | )
63 | }
64 | >
65 | ({post.likes.length})
66 |
67 |
68 | )
69 | }
70 |
71 |
72 | export default LikePost
73 |
--------------------------------------------------------------------------------
/src/components/Post/Post.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react'
2 | import moment from 'moment'
3 | import {
4 | Avatar,
5 | Card,
6 | CardHeader,
7 | IconButton,
8 | Typography,
9 | } from '@material-ui/core'
10 | import PostContent from './PostContent'
11 | import PostFooter from './PostFooter'
12 | import AvartarText from '../UI/AvartarText'
13 | import { MoreHoriz } from '@material-ui/icons'
14 | import { UIContext } from '../../App'
15 |
16 | function Post({ post }) {
17 | const { uiState } = useContext(UIContext)
18 | return (
19 |
25 |
29 |
33 |
34 | ) : (
35 |
39 | )
40 | }
41 | action={
42 |
43 |
44 |
45 | }
46 | title={
47 | post && (
48 |
49 | {post.user.name}
50 |
51 | )
52 | }
53 | subheader={post && moment(post.createdAt).fromNow()}
54 | />
55 |
56 |
57 |
58 | )
59 | }
60 |
61 | export default Post
62 |
--------------------------------------------------------------------------------
/src/components/Post/PostContent.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from 'react'
2 | import { UIContext } from "../../App";
3 | import {
4 | CardContent,
5 | CardMedia,
6 | Typography,
7 | Avatar,
8 | Divider,
9 | useTheme,
10 | useMediaQuery,
11 | } from '@material-ui/core'
12 | import PostSubContent from './PostSubContent'
13 |
14 | function PostContent({ post }) {
15 | const {uiState} = useContext(UIContext)
16 | function isContent() {
17 | return (
18 | post.body.feelings ||
19 | post.body.with.length ||
20 | post.body.at ||
21 | post.body.date
22 | )
23 | }
24 | const theme = useTheme()
25 | const xsScreen = useMediaQuery(theme.breakpoints.only('xs'))
26 | return (
27 |
28 | {isContent() && (
29 |
36 |
37 |
38 | )}
39 |
40 |
43 | {post.content && post.content}
44 |
45 |
46 | {post.image && (
47 |
53 | )}
54 | {Object.keys(post.profilePostData).length ? (
55 | <>
56 |
61 |
62 |
74 |
80 |
81 | >
82 | ) : null}
83 |
84 |
85 | )
86 | }
87 |
88 | export default PostContent
89 |
--------------------------------------------------------------------------------
/src/components/Post/PostFooter.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { Button, Typography, Divider, Grid } from '@material-ui/core'
4 |
5 | import LikePost from './LikePost'
6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
7 | import { faThumbsUp, faPaperPlane } from '@fortawesome/free-regular-svg-icons'
8 | import { UserContext } from '../../App'
9 |
10 | function PostFooter({ post }) {
11 | const { userState } = useContext(UserContext)
12 |
13 | useEffect(() => {
14 | filterLike()
15 | }, [post.likes.length])
16 |
17 | const filterLike = () => {
18 | let users = userState.currentUser.friends.filter((friend) =>
19 | post.likes.includes(friend.id),
20 | )
21 | if (post.likes.includes(userState.currentUser.id)) {
22 | users.push(userState.currentUser)
23 | }
24 |
25 | return users.slice(0, 4)
26 | }
27 |
28 | return (
29 |
30 |
39 |
49 |
56 | {filterLike().length ? (
57 | <>
58 | {filterLike().map((user) => (
59 | {user.name} ,
60 | ))} ...
61 | >
62 | ) : null}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | }
77 | component={Link}
78 | to={`/post/${post.id}`}
79 | >
80 | view
81 |
82 |
83 |
84 |
85 | )
86 | }
87 |
88 | export default PostFooter
89 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/CustomHeaderText.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Typography } from '@material-ui/core'
2 | import React from 'react'
3 | import AvartarText from '../../../UI/AvartarText'
4 |
5 | function CustomHeaderText({ userState, body }) {
6 | function filterUserById(user_id) {
7 | return userState.users.find((usr) => usr.id == user_id)
8 | }
9 | return (
10 |
11 |
12 | {userState.currentUser.name}
13 | {body.feelings ? (
14 | <>
15 | is feeling {body.feelings}
16 | >
17 | ) : null}
18 | {body.with.length ? (
19 | <>
20 | {` with `}
21 |
22 | {body.with.map((u) => (
23 | <> {filterUserById(u).name},>
24 | ))}
25 |
26 | >
27 | ) : null}
28 | {body.at ? (
29 | <>
30 | {` at `} {body.at}
31 | >
32 | ) : null}
33 | {body.date ? (
34 | <>
35 | {new Date(body.date).toLocaleDateString()}
36 | >
37 | ) : null}
38 |
39 | )
40 | }
41 |
42 | export default CustomHeaderText
43 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/DialogHeader.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | CardHeader,
4 | DialogContent,
5 | IconButton,
6 | Paper,
7 | Typography,
8 | } from '@material-ui/core'
9 | import { Close } from '@material-ui/icons'
10 | import React from 'react'
11 | import AvartarText from '../../../UI/AvartarText'
12 | import CustomHeaderText from './CustomHeaderText'
13 |
14 | function DialogHeader({ userState, handleCloseDialog, body }) {
15 | return (
16 |
17 |
21 |
25 |
26 | ) : (
27 |
28 | )
29 | }
30 | title={
31 | <>
32 |
33 | {userState.currentUser.name}
34 |
35 | >
36 | }
37 | action={
38 | handleCloseDialog()}>
39 |
40 |
41 | }
42 | />
43 |
44 |
50 |
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | export default DialogHeader
58 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/FeelingsCard.js:
--------------------------------------------------------------------------------
1 | import { faSmile } from '@fortawesome/free-solid-svg-icons'
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3 | import {
4 | Button,
5 | CardHeader,
6 | Container,
7 | Dialog,
8 | DialogContent,
9 | Grid,
10 | IconButton,
11 | TextField,
12 | Tooltip,
13 | Typography,
14 | } from '@material-ui/core'
15 | import { ArrowBack } from '@material-ui/icons'
16 | import React, { useState } from 'react'
17 |
18 | function FeelingsCard({ body, setBody }) {
19 | const [open, setOpen] = useState(false)
20 | return (
21 | <>
22 |
23 | setOpen(true)}>
24 |
25 |
26 |
27 |
28 |
79 | >
80 | )
81 | }
82 |
83 | export default FeelingsCard
84 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/FileField.js:
--------------------------------------------------------------------------------
1 | import { faImage } from '@fortawesome/free-solid-svg-icons'
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3 | import { IconButton, Tooltip } from '@material-ui/core'
4 | import React from 'react'
5 |
6 | function FileField({fileRef}) {
7 |
8 |
9 | const handleImageClick = (e) => {
10 | e.preventDefault()
11 | fileRef.current.click()
12 | }
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default FileField
24 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/LocationField.js:
--------------------------------------------------------------------------------
1 | import { faMap, faSmile } from '@fortawesome/free-solid-svg-icons'
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3 | import {
4 | Button,
5 | CardHeader,
6 | Container,
7 | Dialog,
8 | DialogContent,
9 | Grid,
10 | IconButton,
11 | TextField,
12 | Tooltip,
13 | Typography,
14 | } from '@material-ui/core'
15 | import { ArrowBack } from '@material-ui/icons'
16 | import React, { useState } from 'react'
17 |
18 | function LocationField({ body, setBody }) {
19 | const [open, setOpen] = useState(false)
20 | return (
21 | <>
22 |
23 | setOpen(true)}>
24 |
25 |
26 |
27 |
28 |
79 | >
80 | )
81 | }
82 |
83 | export default LocationField
84 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/PreviewImage.js:
--------------------------------------------------------------------------------
1 | import { Avatar, CardMedia, IconButton } from '@material-ui/core'
2 | import { Close } from '@material-ui/icons'
3 | import React from 'react'
4 |
5 | function PreviewImage({ previewImage, removeFileImage }) {
6 | return (
7 | <>
8 |
12 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | >
29 | )
30 | }
31 |
32 | export default PreviewImage
33 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/PostDialog/TagUserCard.js:
--------------------------------------------------------------------------------
1 | import { faUserTag } from '@fortawesome/free-solid-svg-icons'
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3 | import React, { useContext, useState } from 'react'
4 | import {
5 | Button,
6 | CardHeader,
7 | Checkbox,
8 | Container,
9 | DialogContent,
10 |
11 | Grid,
12 | IconButton,
13 | List,
14 | ListItem,
15 | ListItemIcon,
16 | ListItemSecondaryAction,
17 | ListItemText,
18 |
19 | Tooltip,
20 | Dialog,
21 | Typography,
22 | } from '@material-ui/core'
23 | import { UserContext } from '../../../../App'
24 | import { ArrowBack } from '@material-ui/icons'
25 |
26 |
27 | function TagUserCard({ body, setBody }) {
28 | const [open, setOpen] = useState(false)
29 | const { userState } = useContext(UserContext)
30 | const [checked, setChecked] = React.useState([])
31 |
32 | const handleToggle = (value) => () => {
33 | const currentIndex = checked.indexOf(value)
34 | const newChecked = [...checked]
35 |
36 | if (currentIndex === -1) {
37 | newChecked.push(value)
38 | } else {
39 | newChecked.splice(currentIndex, 1)
40 | }
41 |
42 | setChecked(newChecked)
43 | }
44 | return (
45 | <>
46 |
47 | {
49 | setOpen(true)
50 | }}
51 | >
52 |
53 |
54 |
55 |
56 |
134 | >
135 | )
136 | }
137 |
138 | export default TagUserCard
139 |
--------------------------------------------------------------------------------
/src/components/Post/PostForm/WritePostCard.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { UIContext, UserContext } from '../../../App'
3 |
4 | import { Paper, Avatar } from '@material-ui/core'
5 |
6 | import PostFormCard from './PostDialog/PostFormCard'
7 | import AvartarText from '../../UI/AvartarText'
8 |
9 | function PostCard({ user }) {
10 | const { userState } = useContext(UserContext)
11 | const { uiState } = useContext(UIContext)
12 |
13 | return (
14 |
15 |
22 |
29 | {userState.currentUser.profile_pic ? (
30 |
31 |
36 |
37 | ) : (
38 |
42 | )}
43 |
48 |
49 |
50 |
51 | )
52 | }
53 |
54 | export default PostCard
55 |
--------------------------------------------------------------------------------
/src/components/Post/PostSubContent.js:
--------------------------------------------------------------------------------
1 | import { Typography } from '@material-ui/core'
2 | import React from 'react'
3 |
4 | function PostSubContent({ post }) {
5 | const isContent = () => {
6 | return (
7 | post.body.feelings ||
8 | post.body.with.length ||
9 | post.body.at ||
10 | post.body.date
11 | )
12 | }
13 | return (
14 |
15 |
16 | {isContent() && post.user.name}
17 | {post.body.feelings ? (
18 | <>
19 | is feeling {post.body.feelings}
20 | >
21 | ) : null}
22 | {post.body.with.length ? (
23 | <>
24 | {` with `}
25 |
26 | {post.body.with.map((u) => (
27 | {u.name},
28 | ))}
29 |
30 | >
31 | ) : null}
32 | {post.body.at ? (
33 | <>
34 | {` at `} {post.body.at}
35 | >
36 | ) : null}
37 | {post.body.date ? (
38 | <>
39 | {new Date(post.body.date).toLocaleDateString()}
40 | >
41 | ) : null}
42 |
43 |
44 | )
45 | }
46 |
47 | export default PostSubContent
48 |
--------------------------------------------------------------------------------
/src/components/Post/Posts.js:
--------------------------------------------------------------------------------
1 | import { Button, Typography } from '@material-ui/core'
2 | import React, { useContext } from 'react'
3 | import { PostContext } from '../../App'
4 | import useFetchPost from '../../hooks/useFetchPost'
5 | import Post from './Post'
6 |
7 | function Posts({ posts }) {
8 | const { postState } = useContext(PostContext)
9 |
10 | const { fetchPosts } = useFetchPost()
11 |
12 | const handleFetchPosts = () => {
13 | fetchPosts()
14 | }
15 | return (
16 |
17 | {posts.length
18 | ? posts.map((post) => (
19 |
22 | ))
23 | : null}
24 |
25 |
28 | {postState.postPagination.totalPage <=
29 | postState.postPagination.currentPage ? (
30 |
31 | No more posts
32 |
33 | ) : (
34 |
41 | )}
42 |
43 |
44 | )
45 | }
46 |
47 | export default Posts
48 |
--------------------------------------------------------------------------------
/src/components/Post/hooks/useCreatePost.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { UIContext, PostContext } from '../../../App'
4 | import { useHistory } from 'react-router-dom'
5 | import { storage } from '../../../firebase/firebase'
6 | const url = process.env.REACT_APP_ENDPOINT
7 |
8 | const useCreatePost = ({
9 | postData,
10 | body,
11 | isImageCaptured,
12 | postImage,
13 | blob,
14 | }) => {
15 | const history = useHistory()
16 |
17 | const [loading, setLoading] = useState(false)
18 | const { uiDispatch } = useContext(UIContext)
19 | const { postDispatch } = useContext(PostContext)
20 |
21 | const createPost = async (data) => {
22 | setLoading(true)
23 | let token = localStorage.token && JSON.parse(localStorage.getItem('token'))
24 | try {
25 | const response = await axios.post(`${url}/api/post/`, data, {
26 | headers: {
27 | Authorization: `Bearer ${token}`,
28 | },
29 | })
30 |
31 | setLoading(false)
32 | postDispatch({ type: 'ADD_POST', payload: response.data.post })
33 | uiDispatch({
34 | type: 'SET_MESSAGE',
35 | payload: {
36 | color: 'success',
37 | display: true,
38 | text: response.data.message,
39 | },
40 | })
41 | uiDispatch({ type: 'SET_POST_MODEL', payload: false })
42 | history.push('/')
43 | } catch (err) {
44 | setLoading(false)
45 | if (err && err.response) {
46 | uiDispatch({
47 | type: 'SET_MESSAGE',
48 | payload: {
49 | display: true,
50 | text: err.response.data.error,
51 | color: 'error',
52 | },
53 | })
54 | }
55 | console.log(err)
56 | }
57 | }
58 |
59 | const createUserPost = async (uri = '') => {
60 | await createPost({
61 | ...postData,
62 | image: uri ? uri : '',
63 | body: {
64 | ...body,
65 | },
66 | })
67 | }
68 |
69 | const handleSubmitPost = (e) => {
70 | e.preventDefault()
71 |
72 | if (isImageCaptured) {
73 | let filename = `post/post-${Date.now()}.png`
74 | const task = storage.ref(`images/${filename}`).put(blob)
75 |
76 | task.on(
77 | 'state_changed',
78 |
79 | function () {
80 | setLoading(true)
81 | },
82 | function (error) {
83 | console.log('error from firebase')
84 | setLoading(false)
85 | uiDispatch({ type: 'SET_POST_MODEL', payload: false })
86 | },
87 | function () {
88 | storage
89 | .ref('images')
90 | .child(filename)
91 | .getDownloadURL()
92 | .then((uri) => {
93 | createUserPost(uri)
94 | setLoading(false)
95 | })
96 | },
97 | )
98 | } else if (postImage) {
99 | let filename = `post/post-${Date.now()}-${postImage.name}`
100 | const uploadTask = storage.ref(`images/${filename}`).put(postImage)
101 | uploadTask.on(
102 | 'state_changed',
103 | () => {
104 | setLoading(true)
105 | },
106 | (err) => {
107 | console.log('error from firebase')
108 | setLoading(false)
109 | uiDispatch({ type: 'SET_POST_MODEL', payload: false })
110 | },
111 | () => {
112 | storage
113 | .ref('images')
114 | .child(filename)
115 | .getDownloadURL()
116 | .then((uri) => {
117 | createUserPost(uri)
118 | })
119 | },
120 | )
121 | } else {
122 | createUserPost()
123 | }
124 | }
125 | return {
126 | handleSubmitPost,
127 | loading,
128 | }
129 | }
130 |
131 | export default useCreatePost
132 |
--------------------------------------------------------------------------------
/src/components/Profile/Friends.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Paper, Grid, Typography, Button, Avatar } from '@material-ui/core'
3 | import AvartarText from '../UI/AvartarText'
4 |
5 | function Friends({ user }) {
6 | return (
7 |
8 | {user.friends &&
9 | user.friends.map((friend) => (
10 |
11 |
19 | {friend.profile_pic ? (
20 |
21 | ) : (
22 |
27 | )}
28 |
33 | {friend.name}
34 |
35 |
40 | {friend.email}
41 |
42 |
52 |
53 |
54 | ))}
55 |
56 | )
57 | }
58 |
59 | export default Friends
60 |
--------------------------------------------------------------------------------
/src/components/Profile/Photos.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Paper, Grid, Avatar, Typography, Button } from '@material-ui/core'
3 |
4 | function Photos() {
5 | return (
6 |
7 |
8 |
9 | Photos
10 |
11 |
12 |
13 |
14 |
15 |
22 | {[...new Array(6)].map((photo) => (
23 |
24 |
25 |
30 |
31 |
32 | ))}
33 |
34 |
35 | )
36 | }
37 |
38 | export default Photos
39 |
--------------------------------------------------------------------------------
/src/components/Profile/PopoverProfile.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Popover from '@material-ui/core/Popover'
3 | import Typography from '@material-ui/core/Typography'
4 | import { makeStyles } from '@material-ui/core/styles'
5 | import PopoverProfileCard from "./PopoverProfileCard"
6 | const useStyles = makeStyles((theme) => ({
7 | popover: {
8 | pointerEvents: 'none',
9 | },
10 | paper: {
11 | padding: theme.spacing(1),
12 | },
13 | }))
14 |
15 | export default function MouseOverPopover({ children,user }) {
16 | const classes = useStyles()
17 | const [anchorEl, setAnchorEl] = React.useState(null)
18 |
19 | const handlePopoverOpen = (event) => {
20 | setAnchorEl(event.currentTarget)
21 | }
22 |
23 | const handlePopoverClose = () => {
24 | setAnchorEl(null)
25 | }
26 |
27 | const open = Boolean(anchorEl)
28 |
29 | return (
30 |
31 |
37 | {children}
38 |
39 |
58 |
59 |
60 |
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/Profile/PopoverProfileCard.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Card,
4 | CardActions,
5 | CardHeader,
6 | Chip,
7 | Grid,
8 | Typography,
9 | } from '@material-ui/core'
10 | import React from 'react'
11 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
12 | import StyledBadge from '../UI/StyledBadge'
13 | import AvartarText from '../UI/AvartarText'
14 |
15 | import {
16 | faMapMarkerAlt,
17 | faUserGraduate,
18 | faUsers,
19 | } from '@fortawesome/free-solid-svg-icons'
20 | function PopoverProfileCard({ user }) {
21 | return (
22 |
23 |
30 |
35 |
36 | ) : (
37 |
41 |
46 |
47 | )
48 | }
49 | title={
50 |
53 | {user.name}
54 |
55 | }
56 | subheader={
57 | <>
58 | {user.location && (
59 |
60 |
61 |
68 |
69 |
70 |
71 |
72 |
73 | {user.location.city || user.location.region}
74 |
75 |
76 |
77 | )}
78 | {user.education && (
79 |
85 |
86 |
93 |
94 |
95 |
96 |
97 |
98 | {user.education}
99 |
100 |
101 |
102 | )}
103 | >
104 | }
105 | />
106 |
107 |
108 |
109 | }
111 | size="medium"
112 | color="primary"
113 | label={`Friends ${user.friends.length}`}
114 | />
115 |
116 |
117 |
118 | )
119 | }
120 |
121 | export default PopoverProfileCard
122 |
--------------------------------------------------------------------------------
/src/components/Profile/ProfileHeader.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { Paper, Typography, makeStyles, Grid, Button } from '@material-ui/core'
3 | import UpdateProfileImage from './UpdateProfileImage'
4 | import UpdateCoverImage from './UpdateCoverImage'
5 | import { UserContext } from '../../App'
6 | const useStyles = makeStyles((theme) => ({
7 | paper: {
8 | width: '100%',
9 | height: '40vh',
10 | marginTop: '60px',
11 | position: 'relative',
12 | backgroundRepeat: 'no-repeat',
13 | backgroundPosition: 'center',
14 | backgroundSize: '100% 40vh',
15 | },
16 |
17 | overlay: {
18 | position: 'absolute',
19 | background: 'rgba(0,0,0,0.5)',
20 | width: '100%',
21 | height: '40vh',
22 | top: 0,
23 | },
24 | }))
25 | function ProfileHeader({ user }) {
26 | const { userState } = useContext(UserContext)
27 | const classes = useStyles()
28 | return (
29 |
30 |
31 |
32 |
41 |
42 | {userState.currentUser.id == user.id && (
43 | <>
44 |
45 | >
46 | )}
47 |
48 |
49 |
50 |
51 |
57 |
70 |
71 | {user.name}
72 |
73 |
74 |
75 |
76 |
77 | )
78 | }
79 |
80 | export default ProfileHeader
81 |
--------------------------------------------------------------------------------
/src/components/Profile/ProfileTimeline.js:
--------------------------------------------------------------------------------
1 | import { Grid} from '@material-ui/core'
2 | import React, { useContext } from 'react'
3 | import WritePostCard from '../Post/PostForm/WritePostCard'
4 | import { UserContext, PostContext } from '../../App'
5 | import Posts from '../Post/Posts'
6 |
7 |
8 | function ProfileTimeline({ user }) {
9 | const { userState } = useContext(UserContext)
10 | const { postState } = useContext(PostContext)
11 |
12 | const getUserPost = () => {
13 | return postState.posts.filter((post) => post.user.id == user.id)
14 | }
15 | return (
16 |
17 |
18 | {userState.currentUser.id == user.id && }
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | export default ProfileTimeline
26 |
--------------------------------------------------------------------------------
/src/components/Profile/UpdateCoverImage.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Button,
4 | DialogActions,
5 | DialogTitle,
6 | IconButton,
7 | Dialog,
8 | DialogContent,
9 | } from '@material-ui/core'
10 | import React, { useState, useRef } from 'react'
11 | import { CameraAlt as CameraIcon } from '@material-ui/icons'
12 | import useUpdateProfilePic from '../../hooks/useUpdateProfilePic'
13 | import DialogLoading from '../UI/DialogLoading'
14 | import { useHistory } from 'react-router-dom'
15 | function UpdateCoverImage() {
16 | const history = useHistory()
17 | const [coverPic, setCoverPic] = useState(null)
18 | const [previewImage, setPreviewImage] = useState(null)
19 | const [menu, setMenu] = useState(false)
20 |
21 | const inputFileRef = useRef(null)
22 |
23 | const { updateCoverPic, loading } = useUpdateProfilePic({
24 | cover_pic: coverPic,
25 | history
26 | })
27 |
28 | const handleImageChange = (e) => {
29 | setCoverPic(e.target.files[0])
30 | const reader = new FileReader()
31 | reader.readAsDataURL(e.target.files[0])
32 | reader.onload = () => {
33 | setPreviewImage(reader.result)
34 | setMenu(true)
35 | }
36 | }
37 |
38 | const handleImageClick = () => {
39 | inputFileRef.current.click()
40 | }
41 |
42 | const handleUpload = () => {
43 | updateCoverPic()
44 | handleCancel()
45 | }
46 |
47 | const handleCancel = () => {
48 | setCoverPic(null)
49 | setPreviewImage(null)
50 | setMenu(false)
51 | }
52 |
53 | return (
54 |
55 |
59 |
60 |
61 |
62 |
63 |
64 | {loading && (
65 |
66 | )}
67 |
68 |
75 |
76 |
105 |
106 | )
107 | }
108 |
109 | export default UpdateCoverImage
110 |
--------------------------------------------------------------------------------
/src/components/Profile/UpdateProfileImage.js:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Badge,
4 | Button,
5 | DialogActions,
6 | DialogTitle,
7 | IconButton,
8 | Dialog,
9 | DialogContent,
10 | } from '@material-ui/core'
11 | import { useHistory } from 'react-router-dom'
12 |
13 | import React, { useContext, useRef, useState } from 'react'
14 | import AvartarText from '../UI/AvartarText'
15 | import useUpdateProfilePic from '../../hooks/useUpdateProfilePic'
16 | import { CameraAlt as CameraIcon } from '@material-ui/icons'
17 | import DialogLoading from '../UI/DialogLoading'
18 | import { UserContext } from '../../App'
19 | function UpdateProfileImage({ user }) {
20 | const {userState} = useContext(UserContext)
21 | const history = useHistory()
22 | const [profilePic, setProfilePic] = useState(null)
23 | const [previewImage, setPreviewImage] = useState(null)
24 | const [menu, setMenu] = useState(false)
25 |
26 | const inputFileRef = useRef(null)
27 |
28 | const { updateProfilePic, loading } = useUpdateProfilePic({
29 | profile_pic: profilePic,
30 | history,
31 | })
32 |
33 | const handleImageChange = (e) => {
34 | setProfilePic(e.target.files[0])
35 | const reader = new FileReader()
36 | reader.readAsDataURL(e.target.files[0])
37 | reader.onload = () => {
38 | setPreviewImage(reader.result)
39 | setMenu(true)
40 | }
41 | }
42 |
43 | const handleImageClick = () => {
44 | inputFileRef.current.click()
45 | }
46 |
47 | const handleUpload = () => {
48 | updateProfilePic()
49 | handleCancel()
50 | }
51 |
52 | const handleCancel = () => {
53 | setProfilePic(null)
54 | setPreviewImage(null)
55 | setMenu(false)
56 | }
57 |
58 | return (
59 |
60 |
67 |
68 |
69 |
70 |
71 | )
72 | }
73 | style={{
74 | position: 'absolute',
75 | bottom: -30,
76 | width: '170px',
77 | height: '170px',
78 | zIndex: 2,
79 | left: '40%',
80 | }}
81 | >
82 | {user.profile_pic ? (
83 |
89 |
90 |
91 | ) : (
92 |
98 | )}
99 |
100 |
107 |
136 |
137 | {loading && (
138 |
139 | )}
140 |
141 | )
142 | }
143 |
144 | export default UpdateProfileImage
145 |
--------------------------------------------------------------------------------
/src/components/Profile/UserProfile.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { MoreHoriz, Edit, SearchOutlined } from '@material-ui/icons'
3 | import { Paper, AppBar, Tabs, Tab, Box, Grid, Divider } from '@material-ui/core'
4 |
5 | import ProfileHeader from './ProfileHeader'
6 | import ProfileTimeline from './ProfileTimeline'
7 | import Friends from './Friends'
8 | import { UIContext } from '../../App'
9 |
10 | function UserProfile({ user, conScreen }) {
11 | const { uiState } = useContext(UIContext)
12 | const [value, setValue] = React.useState(0)
13 | const handleChange = (event, newValue) => {
14 | setValue(newValue)
15 | }
16 | return (
17 |
18 |
24 |
25 |
26 |
27 |
28 |
37 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | )
65 | }
66 |
67 | function TabPanel(props) {
68 | const { children, value, index, ...other } = props
69 |
70 | return (
71 |
72 | {value === index && {children}}
73 |
74 | )
75 | }
76 |
77 | export default UserProfile
78 |
--------------------------------------------------------------------------------
/src/components/Search.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { makeStyles, InputBase } from '@material-ui/core'
3 | import { Search as SearchIcon } from '@material-ui/icons'
4 | import { UIContext } from '../App'
5 | const useStyles = makeStyles((theme) => ({
6 | search: {
7 | position: 'relative',
8 | borderRadius: '50px 50px 50px 50px',
9 | width: '100%',
10 | },
11 |
12 | searchIcon: {
13 | padding: theme.spacing(0, 2),
14 | height: '100%',
15 | position: 'absolute',
16 | pointerEvents: 'none',
17 | display: 'flex',
18 | alignItems: 'center',
19 | justifyContent: 'center',
20 | },
21 | inputRoot: {
22 | padding: '3px 10px 3px 0px',
23 | },
24 | inputInput: {
25 | flexGrow: 1,
26 | paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
27 | },
28 | }))
29 | function Search({ placeholder }) {
30 | const classes = useStyles()
31 | const { uiState } = useContext(UIContext)
32 | return (
33 |
55 | )
56 | }
57 |
58 | export default Search
59 |
--------------------------------------------------------------------------------
/src/components/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import { Drawer, Toolbar, useMediaQuery, useTheme } from '@material-ui/core'
3 | import { UIContext } from '../App'
4 |
5 | function Sidebar({
6 | children,
7 | anchor = 'left',
8 | background = 'white',
9 | boxShadow = true,
10 | drawerWidth = 380,
11 | }) {
12 | const theme = useTheme()
13 | const matches = useMediaQuery(theme.breakpoints.between(960, 1400))
14 | const { uiState } = useContext(UIContext)
15 | return (
16 |
33 |
34 | {children}
35 |
36 | )
37 | }
38 |
39 | export default Sidebar
40 |
--------------------------------------------------------------------------------
/src/components/UI/AvartarText.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Typography } from '@material-ui/core'
2 | import React from 'react'
3 |
4 | function AvartarText({ text, bg, size = '40px', fontSize = '16px' }) {
5 | return (
6 |
9 |
12 | {text.split('')[0]}
13 |
14 |
15 | )
16 | }
17 |
18 | export default AvartarText
19 |
--------------------------------------------------------------------------------
/src/components/UI/DialogLoading.js:
--------------------------------------------------------------------------------
1 | import { Dialog, LinearProgress, Paper, Typography } from '@material-ui/core'
2 | import React, { useState } from 'react'
3 |
4 | function DialogLoading({ loading, text }) {
5 | const [open, setOpen] = useState(loading)
6 | return (
7 |
41 | )
42 | }
43 |
44 | export default DialogLoading
45 |
--------------------------------------------------------------------------------
/src/components/UI/GoogleMap.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useGoogleMaps } from 'react-hook-google-maps'
3 | function GoogleMap({coords,zoom=17}) {
4 | const { ref } = useGoogleMaps(
5 | 'AIzaSyBMZsZfaghR7UAmCwaNU4fHrnFfn7lYtFw',
6 | {
7 | center: coords,
8 | zoom,
9 | },
10 | )
11 | return
12 | }
13 |
14 | export default GoogleMap
15 |
--------------------------------------------------------------------------------
/src/components/UI/StyledBadge.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Badge from '@material-ui/core/Badge'
3 | import { withStyles } from '@material-ui/core/styles'
4 |
5 | const UIBadge = withStyles((theme) => ({
6 | badge: {
7 | right: 8,
8 | top: 32,
9 | border: `2px solid ${theme.palette.background.paper}`,
10 | padding: '5px 5px',
11 | borderRadius: '100%',
12 | },
13 | }))(Badge)
14 |
15 | export default function StyledBadge({ isActive, children,border }) {
16 | return (
17 |
22 | {children}
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/settings/Blocking.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Blocking() {
4 | return (
5 |
6 | Blockings
7 |
8 | )
9 | }
10 |
11 | export default Blocking
12 |
--------------------------------------------------------------------------------
/src/components/settings/General.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react'
2 | import {Divider, Grid, Typography } from '@material-ui/core'
3 | import { UserContext } from '../../App'
4 | import EditInput from './General/EditInput'
5 | import useUpdateProfile from '../../hooks/useUpdateProfile'
6 | function General() {
7 | const { userState } = useContext(UserContext)
8 | const {
9 | loading,
10 | editName,
11 | editEmail,
12 | editBio,
13 | editEducation,
14 | } = useUpdateProfile()
15 |
16 | const [name, setName] = useState(userState.currentUser.name)
17 | const [email, setEmail] = useState(userState.currentUser.email)
18 | const [bio, setBio] = useState(userState.currentUser.bio)
19 | const [education, setEducation] = useState(userState.currentUser.education)
20 |
21 | const handleEditName = () => {
22 | editName(name)
23 | }
24 | const handleEditEmail = () => {
25 | editEmail(email)
26 | }
27 | const handleEditBio = () => {
28 | editBio(bio)
29 | }
30 |
31 | const handleEditEducation = () => {
32 | editEducation(education)
33 | }
34 | return (
35 |
36 |
44 | General Account Setting
45 |
46 |
47 |
48 |
49 |
50 | Name
51 |
52 |
53 |
54 | {userState.currentUser.name}
55 |
56 |
57 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Email
72 |
73 |
74 |
75 | {userState.currentUser.email}
76 |
77 |
78 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | Bio
92 |
93 |
94 |
95 | {userState.currentUser.bio}
96 |
97 |
98 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | Education
112 |
113 |
114 |
115 |
116 | {userState.currentUser.education}
117 |
118 |
119 |
120 |
127 |
128 |
129 |
130 |
131 |
132 | )
133 | }
134 |
135 | export default General
136 |
--------------------------------------------------------------------------------
/src/components/settings/General/EditInput.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import {
3 | Grid,
4 | Typography,
5 | Button,
6 | Dialog,
7 | DialogTitle,
8 | DialogContent,
9 | TextField,
10 | DialogActions,
11 | } from '@material-ui/core'
12 |
13 | function EditInput({ label, input, setInput, editAction, loading }) {
14 | const [dialog, setDialog] = useState(false)
15 | const handleChange = (value) => {
16 | setInput(value)
17 | }
18 |
19 | const handleUpdate = () => {
20 | setDialog(false)
21 | editAction()
22 | }
23 | return (
24 |
25 |
28 |
29 |
49 |
50 | )
51 | }
52 |
53 | export default EditInput
54 |
--------------------------------------------------------------------------------
/src/components/settings/Location.js:
--------------------------------------------------------------------------------
1 | import {
2 | Grid,
3 | List,
4 | ListItem,
5 | ListItemIcon,
6 | ListItemText,
7 | Typography,
8 | } from '@material-ui/core'
9 | import React, { useContext, useState } from 'react'
10 | import { UserContext } from '../../App'
11 | import EditLocation from './Location/EditLocation'
12 | import useUpdateProfile from '../../hooks/useUpdateProfile'
13 | import GoogleMap from '../UI/GoogleMap'
14 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
15 | import {
16 | faCity,
17 | faHouseUser,
18 | faMapPin,
19 | } from '@fortawesome/free-solid-svg-icons'
20 | import { faClock, faMap } from '@fortawesome/free-regular-svg-icons'
21 | function Location() {
22 | const { userState } = useContext(UserContext)
23 | const [location, setLocation] = useState(null)
24 |
25 | const { editLocation, loading } = useUpdateProfile()
26 |
27 | const updateLocation = () => {
28 | editLocation(location)
29 | }
30 | return (
31 |
32 |
33 |
34 |
42 | Location Setting
43 |
44 |
45 |
46 |
52 |
53 |
54 |
55 | {userState.currentUser.location && (
56 |
57 |
58 |
59 |
60 |
61 | {userState.currentUser.location.city}
62 |
63 |
64 |
65 |
66 |
67 |
68 | {userState.currentUser.location.state}
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | {userState.currentUser.location.country}
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | {userState.currentUser.location.lat} {' , '}
85 | {userState.currentUser.location.lng}
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | {userState.currentUser.location.region}
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | {userState.currentUser.location.timezone}
103 |
104 |
105 |
106 | )}
107 |
108 | {userState.currentUser.location && (
109 |
110 |
111 |
117 |
118 |
119 | )}
120 |
121 |
122 | )
123 | }
124 |
125 | export default Location
126 |
--------------------------------------------------------------------------------
/src/components/settings/Location/EditLocation.js:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Dialog,
4 | DialogTitle,
5 | DialogContent,
6 | DialogActions,
7 | CircularProgress,
8 | Typography,
9 | } from '@material-ui/core'
10 | import { Alert } from '@material-ui/lab'
11 | import { LocationOn } from '@material-ui/icons'
12 | import React, { useEffect, useState } from 'react'
13 | import useLocationService from '../../../hooks/useLocationService'
14 |
15 | function EditLocation({ location, setLocation, updateLocation, loading }) {
16 | const [dialog, setDialog] = useState(false)
17 | const {
18 | loading: locationLoading,
19 | error,
20 | data,
21 | setError,
22 | getLocation,
23 | } = useLocationService()
24 |
25 | useEffect(() => {
26 | setLocation(data)
27 | }, [data])
28 |
29 | const handleUpdate = () => {
30 | setDialog(false)
31 | updateLocation()
32 | }
33 |
34 | const handleFetchLocation = () => {
35 | setError(null)
36 | getLocation()
37 | }
38 |
39 | return (
40 | <>
41 |
48 |
49 |
86 | >
87 | )
88 | }
89 |
90 | export default EditLocation
91 |
--------------------------------------------------------------------------------
/src/components/settings/Privacy.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Privacy() {
4 | return (
5 |
6 | Privacy
7 |
8 | )
9 | }
10 |
11 | export default Privacy
12 |
--------------------------------------------------------------------------------
/src/components/settings/SecurityAndLogin.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import {
3 | Grid,
4 | List,
5 | ListItem,
6 | ListItemSecondaryAction,
7 | ListItemText,
8 | Typography,
9 | Divider,
10 | ListItemIcon,
11 | } from '@material-ui/core'
12 |
13 | import useUpdateProfile from '../../hooks/useUpdateProfile'
14 | import { Lock } from '@material-ui/icons'
15 | import EditPassword from './SecurityAndLogin/EditPassword'
16 | function SecurityAndLogin() {
17 | const [password, setPassword] = useState({
18 | currentPassword: '',
19 | newPassword: '',
20 | showNewPassword: false,
21 | showCurrentPassword: false,
22 | })
23 |
24 | const { updatePassword } = useUpdateProfile()
25 |
26 | const handleUpdatePassword = () => {
27 | updatePassword({
28 | newPassword: password.newPassword,
29 | currentPassword: password.currentPassword,
30 | })
31 | }
32 | return (
33 |
34 |
42 | Security And Login
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Change password}
54 | secondary={
55 |
56 | Use a strong password that you're not using elsewhere
57 |
58 | }
59 | />
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 | )
72 | }
73 |
74 |
75 | export default SecurityAndLogin
76 |
--------------------------------------------------------------------------------
/src/components/settings/SecurityAndLogin/EditPassword.js:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Dialog,
4 | DialogTitle,
5 | DialogContent,
6 | DialogActions,
7 | InputAdornment,
8 | IconButton,
9 | OutlinedInput,
10 | } from '@material-ui/core'
11 |
12 | import { Visibility, VisibilityOff } from '@material-ui/icons'
13 | import React, { useState } from 'react'
14 |
15 | function EditPassword({ password, setPassword, updatePassword }) {
16 | const [dialog, setDialog] = useState(false)
17 |
18 | const handleChangeCurrentPassword = (e) => {
19 | setPassword({ ...password, currentPassword: e.target.value })
20 | }
21 | const handleChangeNewPassword = (e) => {
22 | setPassword({ ...password, newPassword: e.target.value })
23 | }
24 |
25 | const handleUpdatePassword = () => {
26 | setDialog(false)
27 | updatePassword()
28 | }
29 |
30 | return (
31 | <>
32 |
39 |
40 |
108 | >
109 | )
110 | }
111 |
112 | export default EditPassword
113 |
--------------------------------------------------------------------------------
/src/components/settings/TrigoInformation.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function TrigoInformation() {
4 | return (
5 |
6 | Trigo TrigoInformation
7 |
8 | )
9 | }
10 |
11 | export default TrigoInformation
12 |
--------------------------------------------------------------------------------
/src/context/ChatContext.js:
--------------------------------------------------------------------------------
1 | export const initialChatState = {
2 | selectedFriend: null,
3 | messages: [],
4 | }
5 |
6 | export const ChatReducer = (state, action) => {
7 | switch (action.type) {
8 | case 'SET_MESSAGES':
9 | return {
10 | ...state,
11 | messages: action.payload,
12 | }
13 | case 'ADD_MESSAGE':
14 | return {
15 | ...state,
16 | messages: [...state.messages, action.payload],
17 | }
18 |
19 | case 'SET_SELECTED_FRIEND':
20 | return {
21 | ...state,
22 | selectedFriend: action.payload,
23 | }
24 |
25 | default:
26 | throw new Error(`action type ${action.type} is undefined`)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/context/PostContext.js:
--------------------------------------------------------------------------------
1 | export const initialPostState = {
2 | posts: [],
3 | postPagination: {
4 | currentPage: 0,
5 | totalPage: 0,
6 | },
7 | post: {
8 | comments: [],
9 | commentPagination: {
10 | currentPage: 0,
11 | totalPage: 0,
12 | },
13 | },
14 | }
15 |
16 | export const PostReducer = (state, action) => {
17 | switch (action.type) {
18 | case 'SET_POSTS':
19 | return {
20 | ...state,
21 | posts: action.payload,
22 | }
23 |
24 | case 'SET_CURRENT_POST':
25 | return {
26 | ...state,
27 | post: action.payload,
28 | }
29 |
30 | case 'REMOVE_CURRENT_POST':
31 | return {
32 | ...state,
33 | post: {
34 | comments: [],
35 | commentPagination: {
36 | currentPage: 0,
37 | totalPage: 0,
38 | },
39 | },
40 | }
41 |
42 | case 'ADD_POST':
43 | return {
44 | ...state,
45 | posts: [action.payload, ...state.posts],
46 | }
47 |
48 | case 'POST_PAGINATION':
49 | return {
50 | ...state,
51 | posts: [...state.posts, ...action.payload.posts],
52 | postPagination: {
53 | ...state.postPagination,
54 | currentPage: action.payload.currentPage,
55 | totalPage: action.payload.totalPage,
56 | },
57 | }
58 |
59 | case 'COMMENT_PAGINATION':
60 | return {
61 | ...state,
62 | post: {
63 | ...state.post,
64 | commentPagination: {
65 | ...state.post.commentPagination,
66 | currentPage: action.payload.currentPage,
67 | totalPage: action.payload.totalPage,
68 | },
69 | comments:
70 | state.post.comments && state.post.comments.length
71 | ? [...state.post.comments, ...action.payload.comments]
72 | : [...action.payload.comments],
73 | },
74 | }
75 |
76 | case 'LIKE_UNLIKE_POST':
77 | let l_postIndex = state.posts.findIndex(
78 | (post) => post.id == action.payload.id,
79 | )
80 | state.posts[l_postIndex] = action.payload
81 | if (state.post.id == action.payload.id) {
82 | state.post = action.payload
83 | }
84 |
85 | return {
86 | ...state,
87 | }
88 |
89 | case 'SET_POST_COMMENTS':
90 | return {
91 | ...state,
92 | post: {
93 | ...state.post,
94 | comments: action.payload,
95 | },
96 | }
97 |
98 | case 'ADD_POST_COMMENT':
99 | return {
100 | ...state,
101 | post: {
102 | ...state.post,
103 | comments: [action.payload, ...state.post.comments],
104 | },
105 | }
106 |
107 | case 'LIKE_UNLIKE_COMMENT':
108 | let index1 = state.post.comments.findIndex(
109 | (comment) => comment.id == action.payload.id,
110 | )
111 | state.post.comments[index1] = action.payload
112 |
113 | return {
114 | ...state,
115 | }
116 |
117 | default:
118 | throw new Error(`action type ${action.type} is undefined`)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/context/UIContext.js:
--------------------------------------------------------------------------------
1 | export const initialUIState = {
2 | mdScreen: false,
3 | drawer: false,
4 | navDrawerMenu: false,
5 | postModel: false,
6 | message: null,
7 | notifications: [],
8 | loading: false,
9 | darkMode: false,
10 | }
11 |
12 | export const UIReducer = (state, action) => {
13 | switch (action.type) {
14 | case 'SET_USER_SCREEN':
15 | return {
16 | ...state,
17 | mdScreen: action.payload,
18 | }
19 |
20 | case 'SET_DRAWER':
21 | return {
22 | ...state,
23 | drawer: action.payload,
24 | }
25 |
26 | case 'SET_MESSAGE':
27 | return {
28 | ...state,
29 | message: action.payload,
30 | }
31 | case 'SET_NAV_MENU':
32 | return {
33 | ...state,
34 | navDrawerMenu: action.payload,
35 | }
36 |
37 | case 'SET_POST_MODEL':
38 | return {
39 | ...state,
40 | postModel: action.payload,
41 | }
42 |
43 | case 'SET_NOTIFICATIONS':
44 | return {
45 | ...state,
46 | notifications: action.payload,
47 | }
48 |
49 | case 'ADD_NOTIFICATION':
50 | return {
51 | ...state,
52 | notifications: [action.payload, ...state.notifications],
53 | }
54 |
55 | case 'SET_LOADING':
56 | return {
57 | ...state,
58 | loading: action.payload,
59 | }
60 | case 'SET_DARK_MODE':
61 | return {
62 | ...state,
63 | darkMode: action.payload,
64 | }
65 |
66 | default:
67 | throw new Error(`action type ${action.type} is undefined`)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/firebase/firebase.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase/app'
2 | import 'firebase/storage'
3 |
4 | const firebaseConfig = {
5 | apiKey: process.env.REACT_APP_API_KEY,
6 | authDomain: process.env.REACT_APP_AUTH_DOMAIN,
7 | projectId: process.env.REACT_APP_PROJECT_ID,
8 | storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
9 | messagingSenderId: process.env.REACT_APP_MESSAGEING_SENDER_ID,
10 | appId: process.env.REACT_APP_APP_ID,
11 | measurementId: process.env.REACT_APP_MEASUREMENT_ID,
12 | }
13 |
14 | firebase.initializeApp(firebaseConfig)
15 |
16 | const storage = firebase.storage()
17 |
18 | export { firebase as default, storage }
19 |
--------------------------------------------------------------------------------
/src/hooks/useCreateComment.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { PostContext, UIContext } from '../App'
4 |
5 | const url = process.env.REACT_APP_ENDPOINT
6 |
7 | const useCreateComment = ({
8 | post_id,
9 | commentText,
10 | setError,
11 | setCommentText,
12 | }) => {
13 | const [loading, setLoading] = useState(false)
14 |
15 | const { postDispatch } = useContext(PostContext)
16 | const { uiDispatch } = useContext(UIContext)
17 |
18 | const createComment = async () => {
19 | setLoading(true)
20 | try {
21 | let token = JSON.parse(localStorage.getItem('token'))
22 | const response = await axios.post(
23 | `${url}/api/post/${post_id}/comment`,
24 | { text: commentText },
25 | {
26 | headers: {
27 | Authorization: `Bearer ${token}`,
28 | },
29 | },
30 | )
31 | if (response.data) {
32 | setCommentText('')
33 | postDispatch({ type: 'ADD_POST_COMMENT', payload: response.data.comment })
34 | uiDispatch({
35 | type: 'SET_MESSAGE',
36 | payload: {
37 | color: 'success',
38 | display: true,
39 | text: response.data.message,
40 | },
41 | })
42 | }
43 | setLoading(false)
44 | } catch (err) {
45 | setLoading(false)
46 | console.log(err)
47 | if (err && err.response) {
48 | if (err.response.status === 422) {
49 | setError(err.response.data.error)
50 | }
51 |
52 | uiDispatch({ type: 'SET_MESSAGE', payload: err.response.data.error })
53 | }
54 | }
55 | }
56 |
57 | return {
58 | createComment,
59 | loading,
60 | }
61 | }
62 |
63 | export default useCreateComment
64 |
--------------------------------------------------------------------------------
/src/hooks/useFetchComments.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { PostContext } from '../App'
4 |
5 | const url = process.env.REACT_APP_ENDPOINT
6 |
7 | const useFetchComments = (post_id) => {
8 | const [loading, setLoading] = useState(false)
9 | const { postDispatch } = useContext(PostContext)
10 |
11 | const fetchComments = async () => {
12 | setLoading(true)
13 | try {
14 | let token = JSON.parse(localStorage.getItem('token'))
15 | const response = await axios.get(`${url}/api/post/${post_id}/comment`, {
16 | headers: {
17 | Authorization: `Bearer ${token}`,
18 | },
19 | })
20 | if (response.data) {
21 | console.log(response.data)
22 | postDispatch({
23 | type: 'SET_COMMENTS',
24 | payload: response.data.comments,
25 | })
26 | }
27 | setLoading(false)
28 | } catch (err) {
29 | setLoading(false)
30 | console.log(err)
31 | }
32 | }
33 |
34 | return {
35 | fetchComments,
36 | loading,
37 | }
38 | }
39 |
40 | export default useFetchComments
41 |
--------------------------------------------------------------------------------
/src/hooks/useFetchPost.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { PostContext } from '../App'
4 |
5 | const url = process.env.REACT_APP_ENDPOINT
6 |
7 | const useFetchPost = () => {
8 | const [loading, setLoading] = useState(false)
9 | const { postState, postDispatch } = useContext(PostContext)
10 |
11 | let token = JSON.parse(localStorage.getItem('token'))
12 |
13 | const fetchComments = async (post_id) => {
14 | if (
15 | postState.post.commentPagination.currentPage >
16 | postState.post.commentPagination.totalPage
17 | ) {
18 | return
19 | }
20 | setLoading(true)
21 | try {
22 | const res = await axios.get(
23 | `${url}/api/post/${post_id}/comment/?page=${postState.post.commentPagination.currentPage}`,
24 | {
25 | headers: {
26 | Authorization: `Bearer ${token}`,
27 | },
28 | },
29 | )
30 | if (res.data) {
31 | postDispatch({
32 | type: 'COMMENT_PAGINATION',
33 | payload: {
34 | currentPage: res.data.pagination.currentPage + 1,
35 | totalPage: res.data.pagination.totalPage,
36 | comments: res.data.comments,
37 | },
38 | })
39 | }
40 | setLoading(false)
41 | } catch (err) {
42 | setLoading(false)
43 | console.log(err)
44 | }
45 | }
46 |
47 | const fetchPosts = async () => {
48 | if (
49 | postState.postPagination.currentPage > postState.postPagination.totalPage
50 | ) {
51 | return
52 | } else {
53 | setLoading(true)
54 | try {
55 | const { data } = await axios.get(
56 | `${url}/api/post/?page=${postState.postPagination.currentPage}`,
57 | {
58 | headers: {
59 | Authorization: `Bearer ${token}`,
60 | },
61 | },
62 | )
63 | if (data) {
64 | postDispatch({
65 | type: 'POST_PAGINATION',
66 | payload: {
67 | currentPage: data.pagination.currentPage + 1,
68 | totalPage: data.pagination.totalPage,
69 | posts: data.posts,
70 | },
71 | })
72 | }
73 | setLoading(false)
74 | } catch (err) {
75 | setLoading(false)
76 | console.log(err)
77 | }
78 | }
79 | }
80 |
81 | return {
82 | fetchPosts,
83 | fetchComments,
84 | loading,
85 | }
86 | }
87 |
88 | export default useFetchPost
89 |
--------------------------------------------------------------------------------
/src/hooks/useFriendActions.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { UserContext, UIContext } from '../App'
4 |
5 | const url = process.env.REACT_APP_ENDPOINT
6 |
7 | const useFriendAction = () => {
8 | const [loading, setLoading] = useState(false)
9 | const { userDispatch } = useContext(UserContext)
10 | const { uiDispatch } = useContext(UIContext)
11 |
12 | const acceptFriendRequest = async (request_id) => {
13 | let token = JSON.parse(localStorage.getItem('token'))
14 |
15 | try {
16 | setLoading(true)
17 |
18 | const { data } = await axios.get(
19 | `${url}/api/user/friend_request/${request_id}/accept`,
20 | {
21 | headers: {
22 | Authorization: `Bearer ${token}`,
23 | },
24 | },
25 | )
26 | setLoading(false)
27 | if (data) {
28 | userDispatch({
29 | type: 'ADD_FRIEND',
30 | payload: data.user,
31 | })
32 | userDispatch({
33 | type: 'REMOVE_FRIENDS_REQUEST_RECEIVED',
34 | payload: request_id,
35 | })
36 |
37 | uiDispatch({
38 | type: 'SET_MESSAGE',
39 | payload: { color: 'success', display: true, text: data.message },
40 | })
41 | }
42 | } catch (err) {
43 | setLoading(false)
44 |
45 | if (err && err.response) {
46 | uiDispatch({
47 | type: 'SET_MESSAGE',
48 | payload: {
49 | color: 'error',
50 | display: true,
51 | text: err.response.data.error,
52 | },
53 | })
54 | }
55 | }
56 | }
57 |
58 | const declineFriendRequest = async (request_id) => {
59 | let token = JSON.parse(localStorage.getItem('token'))
60 |
61 | try {
62 | setLoading(true)
63 |
64 | const { data } = await axios.get(
65 | `${url}/api/user/friend_request/${request_id}/decline`,
66 | {
67 | headers: {
68 | Authorization: `Bearer ${token}`,
69 | },
70 | },
71 | )
72 | setLoading(false)
73 | if (data) {
74 | userDispatch({
75 | type: 'REMOVE_FRIENDS_REQUEST_RECEIVED',
76 | payload: request_id,
77 | })
78 | uiDispatch({
79 | type: 'SET_MESSAGE',
80 | payload: { color: 'success', display: true, text: data.message },
81 | })
82 | }
83 | } catch (err) {
84 | setLoading(false)
85 |
86 | if (err && err.response) {
87 | uiDispatch({
88 | type: 'SET_MESSAGE',
89 | payload: {
90 | color: 'error',
91 | display: true,
92 | text: err.response.data.error,
93 | },
94 | })
95 | }
96 | }
97 | }
98 |
99 | const sendFriendRequest = async (user_id) => {
100 | let token = JSON.parse(localStorage.getItem('token'))
101 |
102 | try {
103 | setLoading(true)
104 |
105 | const { data } = await axios.get(
106 | `${url}/api/user/friend_request/${user_id}/send`,
107 | {
108 | headers: {
109 | Authorization: `Bearer ${token}`,
110 | },
111 | },
112 | )
113 | setLoading(false)
114 | if (data) {
115 | userDispatch({
116 | type: 'ADD_FRIENDS_REQUEST_SENDED',
117 | payload: data.friend,
118 | })
119 | uiDispatch({
120 | type: 'SET_MESSAGE',
121 | payload: { color: 'success', display: true, text: data.message },
122 | })
123 | }
124 | } catch (err) {
125 | setLoading(false)
126 |
127 | if (err && err.response) {
128 | uiDispatch({
129 | type: 'SET_MESSAGE',
130 | payload: {
131 | color: 'error',
132 | display: true,
133 | text: err.response.data.error,
134 | },
135 | })
136 | }
137 | }
138 | }
139 |
140 | const cancelFriendRequest = async (request_id) => {
141 | let token = JSON.parse(localStorage.token)
142 | try {
143 | const { data } = await axios.get(
144 | `${url}/api/user/friend_request/${request_id}/cancel`,
145 | {
146 | headers: {
147 | Authorization: `Bearer ${token}`,
148 | },
149 | },
150 | )
151 | if (data) {
152 | userDispatch({
153 | type: 'REMOVE_FRIENDS_REQUEST_SENDED',
154 | payload: request_id,
155 | })
156 | uiDispatch({
157 | type: 'SET_MESSAGE',
158 | payload: { color: 'success', display: true, text: data.message },
159 | })
160 | }
161 | } catch (err) {
162 | if (err && err.response) {
163 | uiDispatch({
164 | type: 'SET_MESSAGE',
165 | payload: {
166 | color: 'error',
167 | display: true,
168 | text: err.response.data.error,
169 | },
170 | })
171 | }
172 | }
173 | }
174 |
175 | return {
176 | sendFriendRequest,
177 | declineFriendRequest,
178 | acceptFriendRequest,
179 | cancelFriendRequest,
180 | loading,
181 | }
182 | }
183 |
184 | export default useFriendAction
185 |
--------------------------------------------------------------------------------
/src/hooks/useLocationService.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import axios from 'axios'
3 |
4 | function useLocationService() {
5 | const [loading, setLoading] = useState(false)
6 | const [error, setError] = useState(null)
7 | const [data, setData] = useState(null)
8 |
9 | const getLocation = () => {
10 | setLoading(true)
11 | navigator.geolocation.getCurrentPosition(
12 | (position) => {
13 | getCityAndCountry(position)
14 | },
15 | (err) => {
16 | locationError()
17 | },
18 | { timeout: 7000 },
19 | )
20 | }
21 |
22 | function getCityAndCountry(position) {
23 | let apiUrl = `https://geocode.xyz/${position.coords.latitude},${position.coords.longitude}?json=1`
24 | axios
25 | .get(apiUrl)
26 | .then((result) => {
27 | locationSuccess(result.data)
28 | })
29 | .catch((err) => {
30 | locationError()
31 | })
32 | }
33 |
34 | function locationSuccess(result) {
35 | setData({
36 | city: result?.city,
37 | country: result?.country,
38 | lat: result?.latt,
39 | lng: result?.longt,
40 | region: result?.region,
41 | state: result?.state,
42 | timezone: result?.timezone,
43 | })
44 | setLoading(false)
45 | }
46 |
47 | function locationError() {
48 | setError('Could not find location . Enter location manually')
49 |
50 | setLoading(false)
51 | }
52 |
53 | return {
54 | loading,
55 | data,
56 | error,
57 | getLocation,
58 | setError,
59 | }
60 | }
61 |
62 | export default useLocationService
63 |
--------------------------------------------------------------------------------
/src/hooks/useSearchFriends.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import axios from 'axios'
3 |
4 | const url = process.env.REACT_APP_ENDPOINT
5 |
6 | function useSearchFriends() {
7 | const [friends, setFriends] = useState([])
8 | const [loading, setLoading] = useState(false)
9 |
10 | const searchFriends = async (name) => {
11 | try {
12 | setLoading(true)
13 | const res = await axios.get(`${url}/api/user/search?name=${name}`)
14 | setFriends(res.data.users)
15 | setLoading(false)
16 | } catch (err) {
17 | setLoading(false)
18 | console.log(err)
19 | }
20 | }
21 | return {
22 | searchFriends,
23 | friends,
24 | loading
25 | }
26 | }
27 |
28 | export default useSearchFriends
29 |
--------------------------------------------------------------------------------
/src/hooks/useSendMessage.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { ChatContext, UIContext } from '../App'
4 |
5 | const url = process.env.REACT_APP_ENDPOINT
6 |
7 | const useSendMessage = ({ textMessage, setTextMessage }) => {
8 | const [loading, setLoading] = useState(false)
9 |
10 | const { chatState, chatDispatch } = useContext(ChatContext)
11 | const { uiDispatch } = useContext(UIContext)
12 |
13 | const sendMessage = async () => {
14 | setLoading(true)
15 | let friendId = chatState.selectedFriend.id
16 | try {
17 | let token = JSON.parse(localStorage.getItem('token'))
18 | const response = await axios.post(
19 | `${url}/api/user/chat/${friendId}/send`,
20 | { text: textMessage },
21 | {
22 | headers: {
23 | Authorization: `Bearer ${token}`,
24 | },
25 | },
26 | )
27 | if (response.data) {
28 | setTextMessage('')
29 | chatDispatch({ type: 'ADD_MESSAGE', payload: response.data.data })
30 | }
31 | setLoading(false)
32 | } catch (err) {
33 | setLoading(false)
34 | console.log(err)
35 | if (err && err.response) {
36 | if (err.response.status === 422) {
37 | uiDispatch({
38 | type: 'SET_MESSAGE',
39 | payload: {
40 | display: true,
41 | text: err.response.data.error,
42 | color: 'error',
43 | },
44 | })
45 | }
46 | }
47 | }
48 | }
49 |
50 | return {
51 | sendMessage,
52 | loading,
53 | }
54 | }
55 |
56 | export default useSendMessage
57 |
--------------------------------------------------------------------------------
/src/hooks/useTheme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { UIContext } from '../App'
3 | import { useContext } from 'react'
4 |
5 | const useTheme = (darkMode) => {
6 | return createMuiTheme({
7 | active: {
8 | success: 'rgb(63,162,76)',
9 | },
10 | palette: {
11 | type: darkMode ? 'dark' : 'light',
12 | primary: {
13 | main: 'rgb(1,133,243)',
14 | },
15 |
16 | secondary: {
17 | main: 'rgb(63,162,76)',
18 | },
19 | },
20 | })
21 | }
22 |
23 | export default useTheme
24 |
--------------------------------------------------------------------------------
/src/hooks/useUpdateProfilePic.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import axios from 'axios'
3 | import { UserContext, UIContext } from '../App'
4 | import { storage } from '../firebase/firebase'
5 | const url = process.env.REACT_APP_ENDPOINT
6 |
7 | const useUpdateProfilePic = ({ profile_pic, cover_pic, history }) => {
8 | const [loading, setLoading] = useState(false)
9 |
10 | const { userState, userDispatch } = useContext(UserContext)
11 | const { uiDispatch } = useContext(UIContext)
12 |
13 | const updateProfilePic = () => {
14 | let filename = `profile_pic/${userState.currentUser.name}-${Date.now()}-${
15 | profile_pic.name
16 | }`
17 | const uploadTask = storage.ref(`images/${filename}`).put(profile_pic)
18 | uploadTask.on(
19 | 'state_changed',
20 | () => {
21 | setLoading(true)
22 | },
23 | (err) => {
24 | console.log('error from firebase')
25 | setLoading(false)
26 | },
27 | () => {
28 | storage
29 | .ref('images')
30 | .child(filename)
31 | .getDownloadURL()
32 | .then((uri) => {
33 | saveProfilePic(uri)
34 | })
35 | },
36 | )
37 | }
38 |
39 | const updateCoverPic = () => {
40 | let filename = `cover_pic/${userState.currentUser.name}-${Date.now()}-${
41 | cover_pic.name
42 | }`
43 | const uploadTask = storage.ref(`images/${filename}`).put(cover_pic)
44 | uploadTask.on(
45 | 'state_changed',
46 | () => {
47 | setLoading(true)
48 | },
49 | (err) => {
50 | console.log('error from firebase')
51 | setLoading(false)
52 | },
53 | () => {
54 | storage
55 | .ref('images')
56 | .child(filename)
57 | .getDownloadURL()
58 | .then((uri) => {
59 | saveCoverImage(uri)
60 | })
61 | },
62 | )
63 | }
64 |
65 | const saveProfilePic = async (profile_url) => {
66 | setLoading(true)
67 | try {
68 | let token = JSON.parse(localStorage.getItem('token'))
69 | const response = await axios.put(
70 | `${url}/api/user/profile_pic/update`,
71 | { profile_url },
72 | {
73 | headers: {
74 | Authorization: `Bearer ${token}`,
75 | },
76 | },
77 | )
78 | if (response.data) {
79 | uiDispatch({
80 | type: 'SET_MESSAGE',
81 | payload: {
82 | text: response.data.message,
83 | color: 'success',
84 | display: true,
85 | },
86 | })
87 | userDispatch({ type: 'UPDATE_USER', payload: response.data.user })
88 | }
89 | setLoading(false)
90 | history.push('/home')
91 | } catch (err) {
92 | setLoading(false)
93 | console.log(err)
94 | }
95 | }
96 |
97 | const saveCoverImage = async (cover_url) => {
98 | setLoading(true)
99 | try {
100 | let token = JSON.parse(localStorage.getItem('token'))
101 | const response = await axios.put(
102 | `${url}/api/user/cover_pic/update`,
103 | { cover_url },
104 | {
105 | headers: {
106 | Authorization: `Bearer ${token}`,
107 | },
108 | },
109 | )
110 | if (response.data) {
111 | uiDispatch({
112 | type: 'SET_MESSAGE',
113 | payload: {
114 | text: response.data.message,
115 | color: 'success',
116 | display: true,
117 | },
118 | })
119 | userDispatch({ type: 'UPDATE_USER', payload: response.data.user })
120 | }
121 | setLoading(false)
122 | history.push('/home')
123 | } catch (err) {
124 | setLoading(false)
125 | console.log(err)
126 | }
127 | }
128 |
129 | return {
130 | updateProfilePic,
131 | updateCoverPic,
132 |
133 | loading,
134 | }
135 | }
136 |
137 | export default useUpdateProfilePic
138 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | #root,
2 | html,
3 | body {
4 | margin: 0;
5 | }
6 |
7 | * {
8 | box-sizing: border-box;
9 | }
10 | html {
11 | scroll-behavior: smooth;
12 | }
13 |
14 |
15 | .MuiAppBar-root{
16 | z-index: "10000";
17 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom'
3 | import "./index.css"
4 | import App from "./App"
5 |
6 | ReactDOM.render(,document.getElementById("root"))
--------------------------------------------------------------------------------
/src/screens/Auth.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react'
2 | import RecentAccounts from '../components/Auth/RecentAccount/RecentAccounts'
3 | import LoginForm from '../components/Auth/LoginForm'
4 | import SignupForm from '../components/Auth/SignupForm'
5 | import {
6 | Button,
7 | Container,
8 | Divider,
9 | Grid,
10 | Paper,
11 | Typography,
12 | } from '@material-ui/core'
13 | import { UIContext } from '../App'
14 |
15 | const Auth = () => {
16 | const [toggleLoginForm, setToggleLoginForm] = useState(true)
17 | const { uiState } = useContext(UIContext)
18 | return (
19 |
20 |
21 |
28 |
35 | Facebook
36 |
37 |
44 | Recents Login
45 |
46 |
54 | click your picture or add an account
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
71 | {toggleLoginForm ? : }
72 |
73 |
74 |
86 |
87 |
88 |
89 |
90 |
91 | )
92 | }
93 |
94 | export default Auth
95 |
--------------------------------------------------------------------------------
/src/screens/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext, useEffect } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { PostContext, UIContext, UserContext } from '../App'
4 | import {
5 | List,
6 | ListItemIcon,
7 | Avatar,
8 | ListItem,
9 | ListItemText,
10 | useTheme,
11 | useMediaQuery,
12 | } from '@material-ui/core'
13 | import Sidebar from '../components/Sidebar'
14 | import WritePostCard from '../components/Post/PostForm/WritePostCard'
15 |
16 | import { homeLeftItems } from '../Data/Home'
17 |
18 | import DrawerBar from '../components/Navbar/DrawerBar'
19 |
20 | import AvartarText from '../components/UI/AvartarText'
21 |
22 | import Posts from '../components/Post/Posts'
23 | import MyFriendLists from '../components/Friends/MyFriendLists'
24 | import useFetchPost from '../hooks/useFetchPost'
25 |
26 | function Home() {
27 | const { uiState, uiDispatch } = useContext(UIContext)
28 | const { userState } = useContext(UserContext)
29 | const { postState } = useContext(PostContext)
30 | const theme = useTheme()
31 | const match = useMediaQuery(theme.breakpoints.between(960, 1400))
32 |
33 | const { fetchPosts } = useFetchPost()
34 | useEffect(() => {
35 | uiDispatch({ type: 'SET_NAV_MENU', payload: true })
36 | uiDispatch({ type: 'SET_DRAWER', payload: false })
37 |
38 | async function loadPosts() {
39 | await fetchPosts()
40 | }
41 |
42 | loadPosts()
43 |
44 | return () => {
45 | uiDispatch({ type: 'SET_NAV_MENU', payload: false })
46 | uiDispatch({ type: 'SET_DRAWER', payload: false })
47 | }
48 | }, [])
49 |
50 | return (
51 |
52 | {uiState.mdScreen ? (
53 |
54 |
61 |
62 |
67 |
68 | {userState.currentUser.profile_pic ? (
69 |
75 |
80 |
81 | ) : (
82 |
86 | )}
87 |
88 |
92 |
93 | {homeLeftItems.map((list, index) => (
94 |
95 |
96 |
100 |
101 |
102 |
103 | ))}
104 |
105 |
106 |
114 |
115 |
116 |
117 | ) : (
118 |
119 |
120 |
121 | )}
122 |
123 |
136 |
137 | )
138 | }
139 |
140 | export default Home
141 |
--------------------------------------------------------------------------------
/src/screens/Messenger.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from 'react'
2 | import {
3 | Avatar,
4 | Container,
5 | Grid,
6 | Paper,
7 | Typography,
8 | } from '@material-ui/core'
9 | import { ChatContext, UIContext } from '../App'
10 | import Messages from '../components/Chat/Messages'
11 | import DrawerBar from '../components/Navbar/DrawerBar'
12 |
13 | import Friends from '../components/Chat/Friends'
14 | import MessageTextArea from '../components/Chat/MessageTextArea'
15 | import FriendNotSelected from '../components/Chat/FriendNotSelected'
16 | import AvartarText from '../components/UI/AvartarText'
17 |
18 | function Messenger() {
19 | const { uiDispatch, uiState } = useContext(UIContext)
20 | const { chatState, chatDispatch } = useContext(ChatContext)
21 | useEffect(() => {
22 | uiDispatch({ type: 'SET_NAV_MENU', payload: true })
23 | uiDispatch({ type: 'SET_DRAWER', payload: false })
24 |
25 | return () => {
26 | uiDispatch({ type: 'SET_NAV_MENU', payload: false })
27 | uiDispatch({ type: 'SET_DRAWER', payload: false })
28 | chatDispatch({ type: 'SET_SELECTED_FRIEND', payload: null })
29 | }
30 | }, [])
31 |
32 | return (
33 |
40 | {!uiState.mdScreen && (
41 |
42 |
43 |
44 | )}
45 |
46 |
47 |
54 | {uiState.mdScreen && (
55 |
69 |
70 |
71 |
72 |
73 | )}
74 | {chatState.selectedFriend ? (
75 |
89 |
101 | {chatState.selectedFriend.profile_pic ? (
102 |
103 |
107 |
108 | ) : (
109 |
115 | )}
116 |
117 | {chatState.selectedFriend.name}
118 |
119 |
120 |
121 |
136 |
137 |
138 |
139 |
147 |
148 |
149 |
150 | ) : (
151 |
152 | )}
153 |
154 |
155 |
156 |
157 | )
158 | }
159 |
160 | export default Messenger
161 |
--------------------------------------------------------------------------------
/src/screens/Profile.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react'
2 | import { UserContext } from '../App'
3 | import { useParams } from 'react-router-dom'
4 | import { fetchUserById } from "../services/UserServices"
5 | import UserProfile from '../components/Profile/UserProfile'
6 |
7 | function Profile() {
8 | const params = useParams()
9 | const { userState,userDispatch } = useContext(UserContext)
10 | const [user, setUser] = useState(null)
11 |
12 | useEffect(() => {
13 | if (userState.currentUser.id == params.userId) {
14 | setUser(userState.currentUser)
15 | }
16 | else {
17 | let userIndex = userState.users.findIndex(user => user.id == params.userId)
18 | if (userIndex !== -1) {
19 | setUser(userState.users[userIndex])
20 | }
21 | else {
22 | fetchUserById(params.userId).then((res) => {
23 | if (res.data) {
24 | setUser(res.data.user)
25 | userDispatch({type:"ADD_USER",payload:res.data.user})
26 | }
27 | if (res.error) {
28 | console.log(res.error)
29 | }
30 | }).catch(err => console.log(err))
31 | }
32 | }
33 |
34 | }, [params.userId])
35 |
36 | return {user && }
37 | }
38 |
39 | export default Profile
40 |
--------------------------------------------------------------------------------
/src/screens/Settings.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense, useContext, useEffect, useState } from 'react'
2 | import { UIContext } from '../App'
3 | import {
4 | Container,
5 | Divider,
6 | Grid,
7 | List,
8 | ListItem,
9 | ListItemIcon,
10 | ListItemText,
11 | Paper,
12 | Typography,
13 | } from '@material-ui/core'
14 | import { SecurityOutlined,LocationCityOutlined,PersonOutline } from '@material-ui/icons'
15 | import DrawerBar from '../components/Navbar/DrawerBar'
16 | const General = React.lazy(() => import('../components/settings/General'))
17 | const SecurityAndLogin = React.lazy(() =>
18 | import('../components/settings/SecurityAndLogin'),
19 | )
20 |
21 | const Location = React.lazy(() => import('../components/settings/Location'))
22 |
23 | function Settings() {
24 | const { uiState, uiDispatch } = useContext(UIContext)
25 | const [tab, setTab] = useState('general')
26 |
27 | useEffect(() => {
28 | uiDispatch({ type: 'SET_NAV_MENU', payload: true })
29 | uiDispatch({ type: 'SET_DRAWER', payload: true })
30 |
31 | return () => {
32 | uiDispatch({ type: 'SET_NAV_MENU', payload: false })
33 | uiDispatch({ type: 'SET_DRAWER', payload: false })
34 | }
35 | }, [])
36 | const handleTabClick = (tab_data) => {
37 | setTab(tab_data)
38 | uiDispatch({ type: 'SET_DRAWER', payload: false })
39 | }
40 | const ListContents = (
41 | <>
42 |
43 | handleTabClick('general')}
46 | style={{
47 | backgroundColor:
48 | tab === 'general'
49 | ? uiState.darkMode
50 | ? 'rgb(76,76,76)'
51 | : 'rgb(235,237,240)'
52 | : null,
53 | }}
54 | >
55 |
56 |
57 |
58 |
59 |
60 | handleTabClick('security_and_login')}
63 | style={{
64 | backgroundColor:
65 | tab === 'security_and_login'
66 | ? uiState.darkMode
67 | ? 'rgb(76,76,76)'
68 | : 'rgb(235,237,240)'
69 | : null,
70 | }}
71 | >
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | handleTabClick('location')}
83 | style={{
84 | backgroundColor:
85 | tab === 'location'
86 | ? uiState.darkMode
87 | ? 'rgb(76,76,76)'
88 | : 'rgb(235,237,240)'
89 | : null,
90 | }}
91 | >
92 |
93 |
94 |
95 |
96 |
97 |
98 | >
99 | )
100 | return (
101 |
108 | {!uiState.mdScreen && {ListContents}}
109 |
110 | {uiState.mdScreen && (
111 |
112 |
113 | {ListContents}
114 |
115 |
116 | )}
117 |
118 |
119 | Loading}>
120 | {tab === 'general' && }
121 |
122 | {tab === 'security_and_login' && }
123 |
124 | {tab === 'location' && }
125 |
126 |
127 |
128 |
129 |
130 | )
131 | }
132 |
133 | export default Settings
134 |
--------------------------------------------------------------------------------
/src/services/AuthService.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | const url = process.env.REACT_APP_ENDPOINT
3 |
4 | export const fetchCurrentUser = async () => {
5 | let token = localStorage.token && JSON.parse(localStorage.token)
6 |
7 | try {
8 | const { data } = await axios.get(`${url}/api/user/me`, {
9 | headers: {
10 | Authorization: `Bearer ${token}`,
11 | },
12 | })
13 | if (data) {
14 | return {
15 | data,
16 | }
17 | }
18 | } catch (err) {
19 | if (err && err.response) {
20 | return {
21 | error: err.response.data.error,
22 | }
23 | }
24 | }
25 | }
26 |
27 | export const loginUser = async (userData, loading, setLoading) => {
28 | try {
29 | setLoading(true)
30 | const { data } = await axios.post(
31 | `${process.env.REACT_APP_ENDPOINT}/api/auth/login`,
32 | userData,
33 | )
34 | setLoading(false)
35 | if (data) {
36 | return {
37 | data,
38 | }
39 | }
40 | } catch (err) {
41 | setLoading(false)
42 | if (err && err.response) {
43 | return {
44 | error: err.response.data.error,
45 | }
46 | }
47 | }
48 | }
49 |
50 | export const LogoutUser = async () => {
51 | let token = localStorage.token && JSON.parse(localStorage.token)
52 |
53 | try {
54 | const { data } = await axios.get(`${url}/api/auth/logout`, {
55 | headers: {
56 | Authorization: `Bearer ${token}`,
57 | },
58 | })
59 | if (data) {
60 | return {
61 | data,
62 | }
63 | }
64 | } catch (err) {
65 | if (localStorage.token) {
66 | localStorage.removeItem('token')
67 | }
68 | if (err && err.response) {
69 | return {
70 | status: err.response.status,
71 | error: err.response.data.error,
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/services/ChatService.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | const url = process.env.REACT_APP_ENDPOINT
3 |
4 |
5 | export const fetchFriendMessages = async (friend_id) => {
6 | let token = localStorage.token && JSON.parse(localStorage.token)
7 | try{
8 | const res = await axios.get(`${url}/api/user/chat/${friend_id}/get_messages`,{headers:{
9 | Authorization:`Bearer ${token}`
10 | }})
11 | return {
12 | data:res.data
13 | }
14 |
15 | }catch(err){
16 | console.log(err)
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/src/services/PostServices.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | const url = process.env.REACT_APP_ENDPOINT
3 |
4 | export const fetchAllPosts = async () => {
5 | let token = JSON.parse(localStorage.token)
6 |
7 | try {
8 | const response = await axios.get(`${url}/api/post/`, {
9 | headers: {
10 | Authorization: `Bearer ${token}`,
11 | },
12 | })
13 | if (response.data) {
14 | return {
15 | data: response.data,
16 | }
17 | }
18 | } catch (err) {
19 | console.log(err)
20 | }
21 | }
22 |
23 | // export const fetchPostById = async (post_id) => {
24 | // let token = JSON.parse(localStorage.token)
25 |
26 | // try {
27 | // const response = await axios.get(`${url}/api/post/${post_id}`, {
28 | // headers: {
29 | // Authorization: `Bearer ${token}`,
30 | // },
31 | // })
32 | // if (response.data) {
33 | // return {
34 | // data: response.data,
35 | // }
36 | // }
37 | // } catch (err) {
38 | // console.log(err)
39 | // }
40 | // }
41 |
42 | export const likeDislikePost = async (post_id) => {
43 | let token = JSON.parse(localStorage.token)
44 |
45 | try {
46 | const response = await axios.get(
47 | `${url}/api/post/${post_id}/like_dislike`,
48 | {
49 | headers: {
50 | Authorization: `Bearer ${token}`,
51 | },
52 | },
53 | )
54 | if (response.data) {
55 | return {
56 | data: response.data,
57 | }
58 | }
59 | } catch (err) {
60 | console.log(err)
61 | if (err && err.response) {
62 | return {
63 | status: err.response.status,
64 | error: err.response.data.error,
65 | }
66 | }
67 | }
68 | }
69 |
70 | export const likeDislikeComment = async (comment_id) => {
71 | try {
72 | let token = JSON.parse(localStorage.token)
73 | const response = await axios.get(
74 | `${url}/api/post/comment/${comment_id}/like_dislike`,
75 | {
76 | headers: {
77 | Authorization: `Bearer ${token}`,
78 | },
79 | },
80 | )
81 | if (response.data) {
82 | return {
83 | data: response.data,
84 | }
85 | }
86 | } catch (err) {
87 | console.log(err)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/services/UserServices.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | const url = process.env.REACT_APP_ENDPOINT
3 |
4 | export const fetchUserById = async (user_id) => {
5 | let token = JSON.parse(localStorage.token)
6 | try {
7 | const { data } = await axios.get(`${url}/api/user/${user_id}`, {
8 | headers: {
9 | Authorization: `Bearer ${token}`,
10 | },
11 | })
12 | if (data) {
13 | return {
14 | data,
15 | }
16 | }
17 | } catch (err) {
18 | if (err && err.response) {
19 | return {
20 | error: err.response.data.error,
21 | }
22 | }
23 | }
24 | }
25 |
26 | export const fetchRecommandedUsers = async () => {
27 | let token = JSON.parse(localStorage.token)
28 | try {
29 | const { data } = await axios.get(`${url}/api/user/recommanded_users`, {
30 | headers: {
31 | Authorization: `Bearer ${token}`,
32 | },
33 | })
34 | if (data) {
35 | return {
36 | data,
37 | }
38 | }
39 | } catch (err) {
40 | if (err && err.response) {
41 | return {
42 | error: err.response.data.error,
43 | }
44 | }
45 | }
46 | }
47 |
48 |
49 | export const sendFriendRequest = async (user_id) => {
50 | let token = JSON.parse(localStorage.token)
51 | try {
52 | const { data } = await axios.get(`${url}/api/user/friend_request/${user_id}/send`, {
53 | headers: {
54 | Authorization: `Bearer ${token}`,
55 | },
56 | })
57 | if (data) {
58 | return {
59 | data,
60 | }
61 | }
62 | } catch (err) {
63 | if (err && err.response) {
64 | return {
65 | error: err.response.data.error,
66 | }
67 | }
68 | }
69 | }
70 |
71 |
72 | export const fetchIncommingFriendRequests = async () => {
73 | let token = JSON.parse(localStorage.token)
74 | try {
75 | const { data } = await axios.get(`${url}/api/user/friend_request/received`, {
76 | headers: {
77 | Authorization: `Bearer ${token}`,
78 | },
79 | })
80 | if (data) {
81 | return {
82 | data,
83 | }
84 | }
85 | } catch (err) {
86 | if (err && err.response) {
87 | return {
88 | error: err.response.data.error,
89 | }
90 | }
91 | }
92 | }
93 |
94 |
95 |
96 | export const fetchSendedFriendRequests = async () => {
97 | let token = JSON.parse(localStorage.token)
98 | try {
99 | const { data } = await axios.get(`${url}/api/user/friend_request/sended`, {
100 | headers: {
101 | Authorization: `Bearer ${token}`,
102 | },
103 | })
104 | if (data) {
105 | return {
106 | data,
107 | }
108 | }
109 | } catch (err) {
110 | if (err && err.response) {
111 | return {
112 | error: err.response.data.error,
113 | }
114 | }
115 | }
116 | }
117 |
118 |
119 |
120 | export const acceptFriendRequest = async (request_id) => {
121 | let token = JSON.parse(localStorage.token)
122 | try {
123 | const { data } = await axios.get(`${url}/api/user/friend_request/${request_id}/accept`, {
124 | headers: {
125 | Authorization: `Bearer ${token}`,
126 | },
127 | })
128 | if (data) {
129 | return {
130 | data,
131 | }
132 | }
133 | } catch (err) {
134 | if (err && err.response) {
135 | return {
136 | error: err.response.data.error,
137 | }
138 | }
139 | }
140 | }
141 |
142 |
143 | export const declineFriendRequest = async (request_id) => {
144 | let token = JSON.parse(localStorage.token)
145 | try {
146 | const { data } = await axios.get(`${url}/api/user/friend_request/${request_id}/decline`, {
147 | headers: {
148 | Authorization: `Bearer ${token}`,
149 | },
150 | })
151 | if (data) {
152 | return {
153 | data,
154 | }
155 | }
156 | } catch (err) {
157 | if (err && err.response) {
158 | return {
159 | error: err.response.data.error,
160 | }
161 | }
162 | }
163 | }
164 |
165 |
166 |
167 |
168 | export const cancelFriendRequest = async (request_id) => {
169 | let token = JSON.parse(localStorage.token)
170 | try {
171 | const { data } = await axios.get(`${url}/api/user/friend_request/${request_id}/cancel`, {
172 | headers: {
173 | Authorization: `Bearer ${token}`,
174 | },
175 | })
176 | if (data) {
177 | return {
178 | data,
179 | }
180 | }
181 | } catch (err) {
182 | if (err && err.response) {
183 | return {
184 | error: err.response.data.error,
185 | }
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/utils/FilterArray.js:
--------------------------------------------------------------------------------
1 | export const filterArray = (originalArray) => {
2 | var newArray = []
3 | var lookupObject = {}
4 |
5 | for (var i in originalArray) {
6 | lookupObject[originalArray[i].id] = originalArray[i]
7 | }
8 |
9 | for (i in lookupObject) {
10 | newArray.push(lookupObject[i])
11 | }
12 | return newArray
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/utils/ProtectedRoute.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Redirect, Route } from 'react-router-dom'
3 |
4 | function ProtectedRoute({ component: Component, isLoggedIn, ...rest }) {
5 | return (
6 | {
9 | if (isLoggedIn) {
10 | return
11 | } else {
12 | return (
13 |
14 | )
15 | }
16 | }}
17 | />
18 | )
19 | }
20 |
21 | export default ProtectedRoute
22 |
--------------------------------------------------------------------------------