├── .env.temp
├── .gitignore
├── gulpfile.js
├── package.json
├── public
├── assets
│ ├── addamigo.png
│ ├── home.png
│ └── noavatar.jpg
├── favicon.ico
├── index.html
├── manifest.json
└── robots.txt
└── src
├── App.js
├── Components
├── AddAmigo.css
├── AddAmigo.js
├── EmptyChatRoom.css
├── EmptyChatRoom.js
├── Message.css
├── Message.js
├── ProfilePage.css
├── ProfilePage.js
├── SidebarChat.css
└── SidebarChat.js
├── Context
├── AuthActions.js
├── AuthContext.js
└── AuthReducer.js
├── Pages
├── Header.js
├── Home.js
├── HomeCSS
│ ├── ChatRoom.css
│ ├── Home.css
│ └── Sidebar.css
├── Signin-up.css
└── Signin.js
├── index.css
├── index.html
├── index.js
├── reportWebVitals.js
└── setupTests.js
/.env.temp:
--------------------------------------------------------------------------------
1 | REACT_APP_API_URL = "http://95.217.102.97:5000/"
2 | INLINE_RUNTIME_CHUNK=false
3 | GENERATE_SOURCEMAP=false
4 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | .env
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const inlinesource = require('gulp-inline-source');
3 | const replace = require('gulp-replace');
4 |
5 | gulp.task('default', () => {
6 | return gulp
7 | .src('./build/*.html')
8 | .pipe(replace('.js">', '.js" inline>'))
9 | .pipe(replace('rel="stylesheet">', 'rel="stylesheet" inline>'))
10 | .pipe(
11 | inlinesource({
12 | compress: false,
13 | ignore: ['png'],
14 | })
15 | )
16 | .pipe(gulp.dest('./build'));
17 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chatapp",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.11.4",
7 | "@material-ui/icons": "^4.11.2",
8 | "@memberstack/dom": "^1.9.9",
9 | "axios": "^0.21.1",
10 | "css-loader": "^6.7.3",
11 | "downloadjs": "^1.4.7",
12 | "emoji-mart": "3.0.1",
13 | "filesize": "^10.0.6",
14 | "moment": "^2.29.4",
15 | "react": "^17.0.2",
16 | "react-contenteditable": "^3.3.6",
17 | "react-dom": "^17.0.2",
18 | "react-icons": "^4.7.1",
19 | "react-loader-spinner": "^5.3.4",
20 | "react-modal": "^3.16.1",
21 | "react-router-dom": "^5.2.0",
22 | "react-scripts": "4.0.3",
23 | "react-scrollbars-custom": "^4.1.1",
24 | "react-transition-group": "^4.4.5",
25 | "socket.io-client": "^4.1.2",
26 | "style-loader": "^3.3.1",
27 | "timeago.js": "^4.0.2",
28 | "web-vitals": "^1.0.1"
29 | },
30 | "scripts": {
31 | "start": "react-scripts --openssl-legacy-provider start",
32 | "build": "react-scripts --openssl-legacy-provider build",
33 | "build:gulp": "react-scripts --openssl-legacy-provider build && npx gulp",
34 | "build:webpack": "webpack --mode production --config webpack.config.js",
35 | "build:bundle": "webpack --mode production",
36 | "test": "react-scripts test",
37 | "eject": "react-scripts eject"
38 | },
39 | "eslintConfig": {
40 | "extends": [
41 | "react-app",
42 | "react-app/jest"
43 | ]
44 | },
45 | "browserslist": {
46 | "production": [
47 | ">0.2%",
48 | "not dead",
49 | "not op_mini all"
50 | ],
51 | "development": [
52 | "last 1 chrome version",
53 | "last 1 firefox version",
54 | "last 1 safari version"
55 | ]
56 | },
57 | "devDependencies": {
58 | "@babel/core": "^7.20.12",
59 | "@babel/preset-env": "^7.20.2",
60 | "@babel/preset-react": "^7.18.6",
61 | "babel-loader": "^9.1.2",
62 | "path": "^0.12.7"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/public/assets/addamigo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/WebflowChat_FrontEnd/e1a8cbd54780bf35a270174709a0faec61385149/public/assets/addamigo.png
--------------------------------------------------------------------------------
/public/assets/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/WebflowChat_FrontEnd/e1a8cbd54780bf35a270174709a0faec61385149/public/assets/home.png
--------------------------------------------------------------------------------
/public/assets/noavatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/WebflowChat_FrontEnd/e1a8cbd54780bf35a270174709a0faec61385149/public/assets/noavatar.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/WebflowChat_FrontEnd/e1a8cbd54780bf35a270174709a0faec61385149/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
26 | Amigo Chat
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [],
5 | "start_url": ".",
6 | "display": "standalone",
7 | "theme_color": "#000000",
8 | "background_color": "#ffffff"
9 | }
10 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import Signin from "./Pages/Signin.js"
2 | import Home from "./Pages/Home.js"
3 | import { useContext } from "react"
4 | import { AuthContext } from "./Context/AuthContext.js"
5 |
6 | function App() {
7 | const { isLoading } = useContext(AuthContext);
8 | return (
9 |
10 | {isLoading ? (
11 |
12 | ) : (
13 |
14 | )}
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/src/Components/AddAmigo.css:
--------------------------------------------------------------------------------
1 | .add-amigo-open {
2 | width: 300px;
3 | height: 300px;
4 | z-index: 5000;
5 | border-radius: 15px;
6 | flex-direction: column;
7 | background-color: rgb(245, 241, 237);
8 | position: absolute;
9 | top: 0;
10 | bottom: 0;
11 | left: 0;
12 | right: 0;
13 | margin: auto;
14 | -webkit-box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
15 | 0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12);
16 | box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
17 | 0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12);
18 | }
19 |
20 | .add-amigo-close {
21 | display: none;
22 | }
23 |
24 | .close-div {
25 | display: flex;
26 | justify-content: flex-end;
27 | margin: 5px;
28 | }
29 |
30 | .close-div span {
31 | height: 20px;
32 | width: 20px;
33 | border-radius: 50%;
34 | cursor: pointer;
35 | display: flex;
36 | align-items: center;
37 | justify-content: center;
38 | }
39 |
40 | .close-symbol {
41 | font-size: 15px;
42 | font-weight: 600;
43 | margin: auto;
44 | margin-bottom: 3px;
45 | }
46 |
47 | .add-amigo-img{
48 | height: 100px;
49 | margin: 5px 0;
50 | }
51 |
52 | .add-amigo-open form {
53 | display: flex;
54 | flex-direction: column;
55 | align-items: center;
56 | justify-content: space-around;
57 | }
58 |
59 | .add-amigo-open input {
60 | padding: 10px;
61 | font-size: 18px;
62 | outline: none;
63 | border: none;
64 | border-radius: 15px;
65 | display: flex;
66 | align-items: center;
67 | justify-content: center;
68 | background-color: white;
69 | }
70 |
71 | .add-amigo-open button {
72 | padding: 10px;
73 | border-radius: 8px;
74 | outline: none;
75 | border: none;
76 | margin-top: 20px;
77 | color: white;
78 | font-size: 16px;
79 | font-weight: 900;
80 | background-color: #316af3;
81 | cursor: pointer;
82 | }
83 |
84 | .add-amigo-open button:hover {
85 | background-color: white;
86 | color: #316af3;
87 | border: 1px solid #316af3;
88 | transition: all ease 0.5s;
89 | }
90 |
--------------------------------------------------------------------------------
/src/Components/AddAmigo.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import React, { useContext, useState } from 'react'
3 | import { AuthContext } from '../Context/AuthContext';
4 | import './AddAmigo.css'
5 |
6 | function AddAmigo({addchattoggler,addchattoggle}) {
7 |
8 | const [amigousername, setAmigoUsername] = useState()
9 | const { user } = useContext(AuthContext)
10 |
11 | const API_URL = process.env.REACT_APP_API_URL
12 |
13 | const handleSubmit = async (e) => {
14 | e.preventDefault()
15 | try {
16 | const response = await axios.get(`${API_URL}api/users/?username=${amigousername}`)
17 | setAmigoUsername("")
18 | const data = {
19 | senderId: user._id,
20 | receiverId: response.data._id
21 | }
22 | await axios.post(API_URL+'api/chatrooms', data)
23 | }
24 | catch (err) {
25 | }
26 | window.location.reload();
27 | }
28 |
29 | return (
30 |
40 | )
41 | }
42 |
43 | export default AddAmigo
44 |
--------------------------------------------------------------------------------
/src/Components/EmptyChatRoom.css:
--------------------------------------------------------------------------------
1 | .EmptyChatroom {
2 | width: 100%;
3 | height: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: center;
8 | }
9 | .EmptyChatroom img {
10 | width: 250px;
11 | height: auto;
12 | max-width: 500px;
13 | }
14 |
15 | .empty-chatroom-mainhead {
16 | font-size: 20px;
17 | font-weight: 500;
18 | color: black;
19 | letter-spacing: 0;
20 | }
21 |
22 | .empty-chatroom-subhead {
23 | font-size: 18px;
24 | display: flex;
25 | align-items: center;
26 | justify-content: center;
27 | text-align: center;
28 | margin-top: 5px;
29 | padding: 25px;
30 | color: rgb(92, 91, 91);
31 | line-height: 35px;
32 | letter-spacing: 0.8px;
33 | }
34 |
--------------------------------------------------------------------------------
/src/Components/EmptyChatRoom.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./EmptyChatRoom.css";
3 |
4 | function EmptyChatRoom() {
5 | const API_URL = process.env.REACT_APP_API_URL
6 |
7 | return (
8 |
9 |

13 |
Dine meldinger
14 |
15 | );
16 | }
17 |
18 | export default EmptyChatRoom;
19 |
--------------------------------------------------------------------------------
/src/Components/Message.css:
--------------------------------------------------------------------------------
1 | .message-received {
2 | display: flex;
3 | flex-direction: column;
4 | position: relative;
5 | background-color: #ffffff;
6 | font-weight: 500;
7 | width: fit-content;
8 | max-width: 260px;
9 | border-radius: 15px;
10 | padding: 5px 15px 5px 15px;
11 | border-radius: 10px 10px 10px 0px;
12 | margin-bottom: 25px;
13 | font-family: 'Inter', sans-serif;
14 | font-size: 18px;
15 | }
16 |
17 | .message-sent {
18 | display: flex;
19 | flex-direction: column;
20 | position: relative;
21 | margin-left: auto;
22 | width: fit-content;
23 | max-width: 260px;
24 | padding: 5px 15px 5px 15px;
25 | border-radius: 10px 10px 0px 10px;
26 | background-color: #445dfa;
27 | color: white;
28 | font-family: 'Inter', sans-serif;
29 | margin-bottom: 25px;
30 | font-size: 18px;
31 | }
32 |
33 | /* .message-time {
34 | display: flex;
35 | justify-content: flex-end;
36 | font-size: 12px;
37 | margin-top: 3px;
38 | font-weight: 300;
39 | } */
40 |
41 | .message-box {
42 | display: flex;
43 | flex-direction: row;
44 | padding: 15px 20px;
45 | background-color: #fff;
46 | transition: background-color 200ms ease-in;
47 | }
48 |
49 | .message-box:hover {
50 | background-color: #F8F8F8;
51 | }
52 |
53 | .message-image {
54 | width: 50px;
55 | height: 50px;
56 | border-radius: 50%;
57 | margin-right: 10px;
58 | }
59 |
60 | .message-header {
61 | display: flex;
62 | flex-direction: column;
63 | margin-bottom: 5px;
64 | gap: 4px;
65 | }
66 |
67 | .message-user {
68 | font-size: 16px;
69 | font-weight: 500;
70 | color: #333;
71 | }
72 |
73 | .message-time {
74 | font-size: 14px;
75 | color: #74767E;
76 | }
77 |
78 | .message-text {
79 | white-space: pre-line;
80 | font-size: 16px;
81 | font-weight: 400;
82 | line-height: 1.5;
83 | color: #555;
84 | margin: 0;
85 | }
--------------------------------------------------------------------------------
/src/Components/Message.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import moment from "moment";
3 | import { IconButton } from "@material-ui/core";
4 | import axios from 'axios';
5 | import download from 'downloadjs';
6 | import GetAppIcon from '@material-ui/icons/GetApp';
7 | import './Message.css'
8 | import { AuthContext } from "../Context/AuthContext";
9 |
10 | function Message({ message, amigo, own }) {
11 | const { user } = useContext(AuthContext);
12 | const API_URL = process.env.REACT_APP_API_URL
13 |
14 | const checkFile = (text) => {
15 | if (!text) return false;
16 | if (text.startsWith("") && text.endsWith("")) return true;
17 | else return false;
18 | }
19 |
20 | const getFileUrl = (text) => {
21 | if (!text) return '';
22 | return text.replace("", "").replace("", "");
23 | }
24 |
25 | const getFileName = (text) => {
26 | if (!text) return '';
27 | let filename = text.replace("", "").replace("", "");
28 | return filename.slice(Date.now().toString().length);
29 | }
30 |
31 | const handleDownload = async (filename) => {
32 | const response = await axios.get(`${API_URL}api/messages/download`, {
33 | responseType: 'blob',
34 | params: {
35 | filename
36 | }
37 | })
38 | const content = response.headers['content-type'];
39 | download(response.data, getFileName(filename), content);
40 | }
41 |
42 | return (
43 |
44 |

48 |
49 |
50 |
51 | {own ? "Meg" : amigo?.firstname + " " + amigo?.lastname}
52 | {moment(message.createdAt).format("MMM DD YYYY, HH:mm")}
53 |
54 |
55 | {checkFile(message.text) ?
56 |
57 |
{getFileName(message.text)}
58 |
handleDownload(getFileUrl(message.text))}>
59 |
60 |
61 |
62 | :
63 |
{message.text}
64 | }
65 |
66 |
67 | )
68 | }
69 |
70 | export default Message
71 |
--------------------------------------------------------------------------------
/src/Components/ProfilePage.css:
--------------------------------------------------------------------------------
1 | .profile-card-close {
2 | display: none;
3 | }
4 |
5 | .profile-card-open {
6 | max-width: 400px;
7 | height: 500px;
8 | display: flex;
9 | flex-direction: column;
10 | padding: 10px 30px;
11 | position: absolute;
12 | top: 0;
13 | left: 0;
14 | right: 0;
15 | bottom: 0;
16 | margin: auto;
17 | z-index: 5000;
18 | border-radius: 15px;
19 | background-color: rgb(245, 241, 237);
20 | -webkit-box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
21 | 0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12);
22 | box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
23 | 0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12);
24 | }
25 |
26 | .profile-card-open form {
27 | display: flex;
28 | flex-direction: column;
29 | }
30 |
31 | .profile-card-open form label {
32 | padding-top: 15px;
33 | padding-bottom: 5px;
34 | }
35 |
36 | .username-input{
37 | padding: 10px 8px;
38 | margin: 10px 0;
39 | font-size: 18px;
40 | outline: none;
41 | border: none;
42 | border-radius: 15px;
43 | }
44 | .profile-card-open button {
45 | padding: 10px;
46 | border-radius: 8px;
47 | outline: none;
48 | border: 1px solid #ffffff;
49 | margin-top: 20px;
50 | color: white;
51 | font-size: 16px;
52 | font-weight: 900;
53 | background-color: #12a4ff;
54 | cursor: pointer;
55 | }
56 |
57 | .profile-card-open button:hover {
58 | background-color: #4cb5f7;
59 | transition: all ease 0.5s;
60 | }
61 |
62 | .profile-image {
63 | height: 200px;
64 | width: 200px;
65 | border-radius: 50%;
66 | padding: 2px;
67 | display: flex;
68 | object-fit: cover;
69 | justify-content: center;
70 | align-items: center;
71 | margin-left: auto;
72 | margin-right: auto;
73 | }
74 |
75 | .update-profilepic {
76 | color: transparent;
77 | }
78 | .update-profilepic::-webkit-file-upload-button {
79 | visibility: hidden;
80 | }
81 |
82 | .update-profilepic::before {
83 | content: "Update Profile Pic";
84 | color: #fff;
85 | display: inline-block;
86 | background: #fa0261;
87 | padding: 10px 22px;
88 | outline: none;
89 | display: flex;
90 | width: fit-content;
91 | align-items: center;
92 | justify-content: center;
93 | margin: 0 auto;
94 | -webkit-user-select: none;
95 | cursor: pointer;
96 | font-weight: 600;
97 | border-radius: 2px;
98 | outline: none;
99 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12),
100 | 0 3px 1px -2px rgba(0, 0, 0, 0.2);
101 | }
102 | .update-profilepic:focus {
103 | outline: none !important;
104 | }
105 | .update-profilepic:active::before {
106 | transform: scale(0.9) translate(0px, 2px);
107 | box-shadow: inset 4px 4px 5px 0px rgba(0, 0, 0, 0.2);
108 | }
109 |
110 | @media screen and (max-width:768px) {
111 | .profile-card-open{
112 | max-width: 300px;
113 | }
114 | }
115 |
116 | .user-input {
117 | font-size: 20px;
118 | padding: 10px;
119 | background: white;
120 | border-radius: 10px;
121 | line-height: 1;
122 | }
--------------------------------------------------------------------------------
/src/Components/ProfilePage.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import CloseIcon from "@material-ui/icons/Close";
3 | import "./ProfilePage.css";
4 | import { AuthContext } from "../Context/AuthContext";
5 | import axios from "axios";
6 |
7 | function ProfilePage({ toggler, togglestate }) {
8 |
9 | const { user } = useContext(AuthContext);
10 | const [username, setUsername] = useState(user?.firstname + " " + user?.lastname)
11 | const [photo, setPhoto] = useState("")
12 | const API_URL = process.env.REACT_APP_API_URL
13 |
14 | const handleSubmit = async (e) => {
15 | e.preventDefault()
16 | const config = {
17 | headers: {
18 | "Content-Type": "multipart/form-data",
19 | },
20 | };
21 |
22 | const updated_data = new FormData();
23 | updated_data.append("username", username);
24 | if (photo !== "") {
25 | updated_data.append("photo", photo);
26 | }
27 |
28 | try {
29 | await axios.put(API_URL + 'api/users/', {
30 | params: {
31 | id: user?._id
32 | }
33 | }, updated_data, config)
34 | if (user?._id) {
35 | const result = await axios.get(API_URL + "api/users/",
36 | {
37 | params: {
38 | id: user?._id
39 | }
40 | }
41 | )
42 | const data = JSON.stringify(result.data)
43 | localStorage.setItem("user", data)
44 | }
45 | }
46 | catch (err) {
47 | console.log(err)
48 | }
49 | window.location.reload()
50 | }
51 |
52 | return (
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |

61 |
84 |
85 |
86 | );
87 | }
88 |
89 | export default ProfilePage;
90 |
--------------------------------------------------------------------------------
/src/Components/SidebarChat.css:
--------------------------------------------------------------------------------
1 | .sidebarchat{
2 | position: relative;
3 | display: flex;
4 | align-items: center;
5 | justify-content: space-between;
6 | height: 90px;
7 | padding: 10px 15px;
8 | padding-right: 20px;
9 | background-color: none;
10 | border-bottom: 1px solid #EAEAEA;
11 | }
12 |
13 | .sidebarchat-select{
14 | background: #EAF6FE;
15 | }
16 |
17 | .sidebarchat:hover{
18 | background-color: #eaf6fe57;
19 | cursor: pointer;
20 | }
21 |
22 | .sidebar-chatoption {
23 | margin-top: 10px;
24 | }
25 |
26 | .online{
27 | height: 14px;
28 | width: 14px;
29 | border-radius: 50%;
30 | position: absolute;
31 | bottom: 14px;
32 | left: 55px;
33 | background-color: #1DBF73;
34 | border: 3px solid white;
35 | }
36 |
37 | .profile-online {
38 | display: flex;
39 | align-items: center;
40 | gap: 5px;
41 | color: #1DBF73;
42 | }
43 |
44 | .profile-online span {
45 | height: 14px;
46 | width: 14px;
47 | border-radius: 50%;
48 | background-color: #1DBF73;
49 | }
50 |
51 | .offline{
52 | height: 14px;
53 | width: 14px;
54 | border-radius: 50%;
55 | position: absolute;
56 | bottom: 14px;
57 | left: 55px;
58 | background-color: #B5B6BA;
59 | border: 3px solid white;
60 | }
61 |
62 | .profile-offline {
63 | display: flex;
64 | align-items: center;
65 | gap: 5px;
66 | color: #B5B6BA;
67 | }
68 |
69 | .profile-offline span {
70 | height: 14px;
71 | width: 14px;
72 | border-radius: 50%;
73 | background-color: #B5B6BA;
74 | }
75 |
76 | .sidebarchat-info-name{
77 | text-overflow: ellipsis;
78 | white-space: nowrap;
79 | overflow: hidden;
80 | font-size: 16px;
81 | font-weight: 600;
82 | margin: 0;
83 | width: 140px;
84 | color: #333;
85 | }
86 |
87 | .sidebarchat-plan {
88 | color: #74767E;
89 | font-size: 14px;
90 | }
91 |
92 | .amigo-profilepic{
93 | height: 55px;
94 | width: 55px;
95 | border-radius: 50%;
96 | object-fit: cover;
97 | object-position: center;
98 | margin-right: 15px;
99 | }
100 |
101 | .chatroom-profilepic {
102 | height: 60px;
103 | width: 60px;
104 | border-radius: 50%;
105 | object-fit: cover;
106 | object-position: center;
107 | margin-right: 15px;
108 | }
109 |
110 | .badge {
111 | display: flex;
112 | justify-content: center;
113 | align-items: center;
114 | width: 20px;
115 | height: 20px;
116 | background: #1DBF73;
117 | color: white;
118 | border-radius: 50%;
119 | font-size: 15px;
120 | line-height: 1;
121 | }
122 |
123 | .sidebarchat-plans {
124 | text-overflow: ellipsis;
125 | white-space: nowrap;
126 | overflow: hidden;
127 | width: 140px;
128 | display: flex;
129 | flex-direction: column;
130 | }
131 |
132 | .btn-favorite {
133 | background: transparent;
134 | border: none;
135 | padding: 0;
136 | }
137 |
138 | .latest_time {
139 | color: #74767E;
140 | font-size: 13px;
141 | text-overflow: ellipsis;
142 | white-space: nowrap;
143 | overflow: hidden;
144 | line-height: 1.5;
145 | }
--------------------------------------------------------------------------------
/src/Components/SidebarChat.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from 'react'
2 | import './SidebarChat.css'
3 | import axios from 'axios'
4 | import { io } from "socket.io-client"
5 | import { FaStar } from "react-icons/fa"
6 | import { FiStar } from "react-icons/fi"
7 | import { format } from "timeago.js"
8 | import { localeFunc } from '../Pages/utils'
9 |
10 | function SidebarChat({ chatroomtile, currentChat, currentUser, arrivalChat }) {
11 | const [user, setUser] = useState(null)
12 | const [isSelected, SetIsSelected] = useState(false);
13 | const [online, setOnline] = useState(false);
14 | const [favorite, setFavorite] = useState(false);
15 | const [latestAt, setLatestAt] = useState('');
16 | const [unread, setUnread] = useState(0);
17 | const socket = useRef()
18 | const chatRef = useRef()
19 |
20 | const API_URL = process.env.REACT_APP_API_URL
21 |
22 | const getAmigodetails = async (amigoId) => {
23 | try {
24 | if (amigoId) {
25 | const response = await axios.get(API_URL + 'api/users/user/',
26 | {
27 | params: {
28 | id: amigoId
29 | }
30 | }
31 | )
32 | setUser(response.data)
33 | }
34 | }
35 | catch (err) {
36 | console.log(err)
37 | }
38 | }
39 | const getLatestMessage = async () => {
40 | try {
41 | const response = await axios.get(API_URL + 'api/messages/latest/' + chatroomtile?._id)
42 | const data = response.data;
43 | if (data.length > 0) {
44 | setLatestAt(localeFunc(format(data[0].createdAt)));
45 | }
46 | } catch (err) {
47 | console.log(err);
48 | }
49 | }
50 |
51 | const getUnreadCount = async () => {
52 | try {
53 | const resp = await axios.get(API_URL + 'api/messages/count/', {
54 | params: {
55 | chatroomId: chatroomtile?._id,
56 | userId: currentUser?._id
57 | }
58 | })
59 | setUnread(resp.data);
60 | if (resp.data > 0) {
61 | document.title = "Upit Chat" + ` (${resp.data})`;
62 | } else {
63 | document.title = "Upit Chat";
64 | }
65 | } catch (err) {
66 | console.log(err)
67 | }
68 | }
69 |
70 | const handleFavorite = () => {
71 | const storage = localStorage.getItem("favorites");
72 | let favorites = {}
73 | if (storage) {
74 | favorites = JSON.parse(storage);
75 | }
76 | if (favorites && chatroomtile?._id) {
77 | favorites[chatroomtile?._id] = !favorite;
78 | }
79 | setFavorite(!favorite);
80 | localStorage.setItem("favorites", JSON.stringify(favorites));
81 | }
82 |
83 | useEffect(() => {
84 | const storage = localStorage.getItem("favorites");
85 | let favorites = {};
86 | if (storage) {
87 | favorites = JSON.parse(storage);
88 | }
89 | if (favorites && chatroomtile?._id) {
90 | setFavorite(favorites[chatroomtile?._id]);
91 | }
92 | }, [chatroomtile])
93 |
94 | useEffect(() => {
95 | const amigoId = chatroomtile.members.find((m) => m !== currentUser._id);
96 | const currentId = currentChat?.members.find((m) => m !== currentUser._id);
97 | if (amigoId === currentId) {
98 | SetIsSelected(true);
99 | } else {
100 | SetIsSelected(false);
101 | }
102 |
103 | chatRef.current = currentChat;
104 |
105 | getAmigodetails(amigoId)
106 | getUnreadCount()
107 | getLatestMessage()
108 | }, [currentUser, currentChat, chatroomtile, online, API_URL])
109 |
110 | useEffect(() => {
111 | socket.current = io(API_URL);
112 | const amigoId = chatroomtile.members.find((m) => m !== currentUser._id);
113 | socket.current.on("getUsers", (users) => {
114 | setOnline(users.find((user) => user.userId === amigoId));
115 | })
116 | }, [API_URL, chatroomtile])
117 |
118 | useEffect(() => {
119 | if (document.hidden || chatroomtile?._id !== currentChat?._id) {
120 | getUnreadCount();
121 | }
122 | }, [arrivalChat, currentChat])
123 |
124 | return (
125 |
126 |
127 |

128 |
129 |
130 |
{user ? user?.firstname + " " + user?.lastname : ""}
131 |
132 | {user?.plans.length === 1 && (
133 |
134 | {user.plans[0].planName}
135 |
136 | )}
137 | {user?.plans.length > 1 && (
138 | <>
139 |
140 | {user?.plans[0].planName}
141 |
142 | {/*
143 | +{user?.plans.length - 1} more
144 | */}
145 | >
146 | )}
147 |
148 |
149 |
150 |
151 |
152 | {latestAt ? latestAt.replace(' ago', '') : 'Akkurat nå'}
153 |
154 |
155 | {unread > 0 && {unread}}
156 |
159 |
160 |
161 |
162 | )
163 | }
164 |
165 | export default SidebarChat
--------------------------------------------------------------------------------
/src/Context/AuthActions.js:
--------------------------------------------------------------------------------
1 | export const LoginStart = (userCredentials) =>({
2 | type:"LOGIN_START"
3 | });
4 |
5 | export const LoginSuccess = (user) =>({
6 | type:"LOGIN_SUCCESS",
7 | payload: user
8 | })
9 |
10 | export const LoginFailure = (error) =>({
11 | type:"LOGIN_FAILURE",
12 | payload: error
13 | })
--------------------------------------------------------------------------------
/src/Context/AuthContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useState, useEffect, useReducer } from "react"
2 | import AuthReducer from "./AuthReducer"
3 |
4 | const INITIAL_STATE = {
5 | user: null,
6 | isFetching:false,
7 | error:false
8 | }
9 |
10 | /* Reads the data from the Provider and changes INITIAL_STATE */
11 | export const AuthContext = createContext(INITIAL_STATE)
12 |
13 | /* Children here are the Components that need to get the data.[In this Application we specified App COmponent as Child in index.js so that we can server every every component exist in the app */
14 | /* This will provide data to all the children that we are giving here */
15 | export const AuthContextProvider = ({children}) =>{
16 | const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
17 | const [isLoading, setLoading] = useState(true);
18 | const [currentMem, setCurrentMem] = useState('');
19 |
20 | useEffect(()=>{
21 | if (typeof state.user !== "string") {
22 | localStorage.setItem("user", JSON.stringify(state.user))
23 | }
24 | },[state.user])
25 |
26 | return (
27 |
39 | {children}
40 |
41 | )
42 | }
--------------------------------------------------------------------------------
/src/Context/AuthReducer.js:
--------------------------------------------------------------------------------
1 | const AuthReducer = (state, action) => {
2 | switch (action.type) {
3 | case "LOGIN_START":
4 | return {
5 | user: action.payload,
6 | isLoading: true,
7 | error: false
8 | };
9 | case "LOGIN_SUCCESS":
10 | return {
11 | user: action.payload,
12 | isLoading: false,
13 | error: false
14 | };
15 | case "LOGIN_FAILURE":
16 | return {
17 | user: null,
18 | isLoading: false,
19 | error: action.payload
20 | };
21 |
22 | default:
23 | return state
24 | }
25 | }
26 |
27 | export default AuthReducer;
--------------------------------------------------------------------------------
/src/Pages/Header.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/WebflowChat_FrontEnd/e1a8cbd54780bf35a270174709a0faec61385149/src/Pages/Header.js
--------------------------------------------------------------------------------
/src/Pages/Home.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useRef, useState } from "react";
2 | import { Scrollbar } from "react-scrollbars-custom";
3 | import "./HomeCSS/Home.css";
4 | import "./HomeCSS/Sidebar.css";
5 | import "./HomeCSS/ChatRoom.css";
6 | import Message from "../Components/Message.js";
7 | import AddAmigo from "../Components/AddAmigo.js";
8 | import ProfilePage from "../Components/ProfilePage";
9 | import SidebarChat from "../Components/SidebarChat.js";
10 | import EmptyChatRoom from "../Components/EmptyChatRoom";
11 | import { AuthContext } from "../Context/AuthContext";
12 |
13 | import axios from "axios";
14 | import { io } from "socket.io-client";
15 | import "emoji-mart/css/emoji-mart.css";
16 | import { Picker } from "emoji-mart";
17 | import TextareaAutosize from "@material-ui/core/TextareaAutosize";
18 | import moment from "moment";
19 | import Modal from 'react-modal';
20 | import { filesize } from 'filesize';
21 |
22 | import { IoSend } from "react-icons/io5";
23 | import { AiFillSafetyCertificate } from "react-icons/ai";
24 | import MenuIcon from "@material-ui/icons/Menu";
25 | import CloseIcon from "@material-ui/icons/Close";
26 | import SearchIcon from "@material-ui/icons/Search";
27 | import { Button, IconButton } from "@material-ui/core";
28 | import ExitToAppIcon from "@material-ui/icons/ExitToApp";
29 | import PersonAddIcon from "@material-ui/icons/PersonAdd";
30 | import AttachFileIcon from "@material-ui/icons/AttachFile";
31 | import InsertEmoticonIcon from "@material-ui/icons/InsertEmoticon";
32 | import { format } from "timeago.js"
33 | import { localeFunc } from '../Pages/utils'
34 |
35 | const MAX_FILE_SIZE = 10_485_760;
36 |
37 | function Home() {
38 | const [chatroomtiles, setChatroomtiles] = useState([]);
39 | const [currentchat, setCurrentchat] = useState(null);
40 | const [arrivalChat, setArrivalChat] = useState(null);
41 | const [messages, setMessages] = useState([]);
42 | const [newMessage, setNewMessage] = useState("");
43 | const [search, setSearch] = useState("");
44 | const [online, setOnline] = useState(false);
45 | const [arrivalMessage, setArrivalMessage] = useState(null);
46 | const [amigo, setAmigo] = useState();
47 | const [amigoDetail, setAmigoDetail] = useState();
48 | const [open, setOpen] = useState(false);
49 | const [file, setFile] = useState(null);
50 | const [fileModal, setFileModal] = useState(false);
51 | const { user, currentMem } = useContext(AuthContext);
52 | const API_KEY = process.env.REACT_APP_MEMBERSTACK_KEY;
53 | const BASE_URL = 'https://admin.memberstack.com/members';
54 | const headers = { "X-API-KEY": API_KEY };
55 | const roomRef = useRef();
56 | const socket = useRef();
57 | const chatRef = useRef();
58 |
59 | const API_URL = process.env.REACT_APP_API_URL
60 |
61 | function sendNotification(message, avatar, firstname) {
62 | if (document.hidden) {
63 | const notification = new Notification("New message from Upit Chat", {
64 | icon: avatar,
65 | body: `@${firstname}: ${message}`
66 | })
67 | }
68 | }
69 |
70 | function checkPageStatus(message, avatar, firstname) {
71 | if (!("Notification" in window)) {
72 | alert("This browser does not support system notifications!")
73 | } else if (Notification.permission === "granted") {
74 | sendNotification(message, avatar, firstname)
75 | } else if (Notification.permission !== "denied") {
76 | Notification.requestPermission((permission) => {
77 | if (permission === "granted") {
78 | sendNotification(message, avatar, firstname)
79 | }
80 | })
81 | }
82 | }
83 |
84 | const setRead = async (roomId, userId) => {
85 | if (roomId) {
86 | await axios.put(API_URL + 'api/messages/', {
87 | roomId,
88 | userId
89 | })
90 | }
91 | }
92 |
93 | const getUnreadTotalCount = async () => {
94 | try {
95 | const resp = await axios.get(API_URL + 'api/messages/total/', {
96 | params: {
97 | userId: user?._id
98 | }
99 | })
100 | if (resp?.data.length > 0) {
101 | document.title = "Upit Chat" + ` (${resp.data[0].unread})`;
102 | } else {
103 | document.title = "Upit Chat";
104 | }
105 | } catch (err) {
106 | console.log(err)
107 | }
108 | }
109 |
110 | useEffect(() => {
111 | socket.current = io(API_URL);
112 | socket.current.on("getMessage", (data) => {
113 | setArrivalMessage({
114 | sender: data.senderId,
115 | text: data.text,
116 | createdAt: Date.now(),
117 | });
118 | setArrivalChat(!arrivalChat);
119 | checkPageStatus(data.text, data.avatar, data.firstname);
120 | if (!document.hidden && data.chatroomId === chatRef.current?._id) {
121 | setRead(data.chatroomId, user?._id);
122 | }
123 | if (document.hidden) {
124 | getUnreadTotalCount();
125 | }
126 | });
127 | }, [API_URL]);
128 |
129 | useEffect(() => {
130 | arrivalMessage &&
131 | currentchat?.members.includes(arrivalMessage.sender) &&
132 | setMessages((prev) => [...prev, arrivalMessage]);
133 | }, [arrivalMessage, currentchat]);
134 |
135 | useEffect(() => {
136 | socket.current.emit("addUser", user?._id);
137 | }, [user, chatroomtiles, currentchat, socket]);
138 |
139 | /* Fetching the Chat Tiles */
140 | useEffect(() => {
141 | const getChatroomtiles = async (searchText) => {
142 | try {
143 | let data = null;
144 | if (currentMem) {
145 | const response = await axios.get(`${API_URL}api/users/?member=${currentMem}`)
146 | data = {
147 | senderId: user._id,
148 | receiverId: response.data._id
149 | }
150 | await axios.post(API_URL + 'api/chatrooms', data)
151 | }
152 |
153 | const res = await axios.get(API_URL + "api/chatrooms", {
154 | params: {
155 | user_id: user._id
156 | }
157 | });
158 | setChatroomtiles(res.data);
159 |
160 | if (data) {
161 | const resp = await axios.post(API_URL + 'api/chatrooms/get', data);
162 | chatRef.current = resp.data[0];
163 | setCurrentchat(resp.data[0]);
164 | }
165 | } catch (err) {
166 | console.log(err);
167 | }
168 | };
169 | (async () => {
170 | await getChatroomtiles();
171 | })();
172 | }, [user?._id, currentMem, API_URL]);
173 |
174 | /* Fetching the Chat Tile user details */
175 | useEffect(() => {
176 | const amigoId = currentchat?.members.find((m) => m !== user._id);
177 | socket.current.on("getUsers", (users) => {
178 | setOnline(users.find((user) => user.userId === amigoId));
179 | })
180 | const getUserInfo = async (mem_id) => {
181 | try {
182 | const resp = await axios.get(`${BASE_URL}/${mem_id}`, { headers });
183 | return resp.data.data;
184 | } catch (err) {
185 | console.log(err);
186 | }
187 | return null;
188 | }
189 | const getAmigodetails = async () => {
190 | try {
191 | if (amigoId) {
192 | const response = await axios.get(API_URL + "api/users/user",
193 | {
194 | params: {
195 | id: amigoId
196 | }
197 | }
198 | );
199 | const amigoData = response.data;
200 | setAmigo(amigoData);
201 | const response2 = await getUserInfo(amigoData.mem_id);
202 | setAmigoDetail(response2);
203 | }
204 | } catch (err) { }
205 | };
206 | if (currentchat) {
207 | getAmigodetails();
208 | }
209 | }, [user, currentchat, API_URL]);
210 |
211 | /* Fetching ChatRoom Messages */
212 | useEffect(() => {
213 | const getMessages = async () => {
214 | try {
215 | if (user && currentchat) {
216 | setRead(currentchat?._id, user?._id);
217 | }
218 | const response = await axios.get(API_URL + "api/messages/" + currentchat?._id);
219 | setMessages(response.data);
220 | } catch (err) {
221 | console.log(err);
222 | }
223 | };
224 | if (currentchat) {
225 | getMessages();
226 | }
227 | }, [currentchat, API_URL]);
228 |
229 | /* Scroll to the recent message */
230 | useEffect(() => {
231 | scrollItThere();
232 | }, [messages]);
233 |
234 | const scrollItThere = () => {
235 | roomRef.current?.scroll({
236 | top: roomRef.current?.scrollHeight,
237 | behavior: 'smooth'
238 | });
239 | }
240 |
241 | /* Emoji Picker */
242 | const addEmoji = (e) => {
243 | let emoji = e.native;
244 | setNewMessage(newMessage + emoji);
245 | };
246 | const [pick, setPick] = useState(false);
247 | const openPicker = () => {
248 | setPick(!pick);
249 | };
250 |
251 | /* Posting a Message */
252 |
253 | const handleMessageKey = (e) => {
254 | if (e.keyCode == 13 && !e.shiftKey) {
255 | e.preventDefault();
256 | handleSubmit(e);
257 | return false;
258 | }
259 | }
260 |
261 | const handleMessage = (e) => {
262 | setNewMessage(e.target.value);
263 | }
264 |
265 | const handleSubmit = async (e, fileName = "") => {
266 | e?.preventDefault();
267 | let textMessage = newMessage || fileName;
268 | if (!textMessage) return;
269 | const sendingMessage = {
270 | chatroomId: currentchat._id,
271 | senderId: user._id,
272 | text: textMessage,
273 | };
274 |
275 | const receiverId = currentchat.members.find(
276 | (member) => member !== user._id
277 | );
278 | socket.current.emit("sendMessage", {
279 | senderId: user._id,
280 | receiverId,
281 | chatroomId: currentchat?._id,
282 | firstname: user?.firstname,
283 | avatar: user?.avatar ? user.avatar : API_URL + "api/images/noavatar.png",
284 | text: textMessage,
285 | });
286 | try {
287 | if (user && currentchat) {
288 | setRead(currentchat?._id, user?._id);
289 | setArrivalChat(!arrivalChat);
290 | }
291 | const response = await axios.post(API_URL + "api/messages/", sendingMessage);
292 | setMessages([...messages, response.data]);
293 | setNewMessage("");
294 | } catch (err) {
295 | console.log(err);
296 | }
297 | setPick(false)
298 | };
299 |
300 | const handleFileUpload = async (e) => {
301 | const onefile = e.target.files[0];
302 | if (onefile?.size < MAX_FILE_SIZE) {
303 | setFile(onefile);
304 | setFileModal(true);
305 | } else {
306 | window.alert(`Max file size: 10 MB\nCurrent file size: ${filesize(onefile?.size, { base: 2, standard: "jedec" })}`)
307 | }
308 | }
309 |
310 | const handleFileSend = async (e) => {
311 | const config = {
312 | headers: {
313 | "Content-Type": "multipart/form-data",
314 | },
315 | };
316 | const sendData = new FormData();
317 | sendData.append("chatroomId", currentchat._id);
318 | sendData.append("senderId", user._id);
319 | sendData.append("fileData", file);
320 | sendData.append("fileName", file.name);
321 |
322 | try {
323 | axios.post(API_URL + 'api/messages/upload', sendData, config, {
324 | onUploadProgress: (event) => {
325 | const progress = Math.round((event.loaded / event.total) * 100);
326 | console.log(`Progress: ${progress}%`);
327 | }
328 | }).then(resp => {
329 | setNewMessage("")
330 | handleSubmit(e, resp.data.text);
331 | setFileModal(false);
332 | })
333 | }
334 | catch (err) {
335 | console.log(err)
336 | }
337 | }
338 |
339 | /* Logout */
340 | const logout = () => {
341 | localStorage.removeItem("user");
342 | window.location.href = "/";
343 | };
344 |
345 | /* AddChat Toggle Setup */
346 |
347 | const [addtoggle, setAddtoggle] = useState(false);
348 | const addchatToggler = () => {
349 | addtoggle === false ? setAddtoggle(true) : setAddtoggle(false);
350 | };
351 |
352 | /* Profile Page Toggle Setup */
353 | const [profiletoggle, setProfiletoggle] = useState(false);
354 | const profiletoggler = () => {
355 | profiletoggle === false ? setProfiletoggle(true) : setProfiletoggle(false);
356 | };
357 |
358 | const handleCloseModal = () => {
359 | setFileModal(false);
360 | setFile("");
361 | }
362 |
363 | return (
364 |
365 | {/* Chat Adding Card */}
366 |
{ addchatToggler(); }} addchattoggle={addtoggle} />
367 |
368 | {/* Profile Page Card - Update */}
369 | { profiletoggler(); }} togglestate={profiletoggle} />
370 |
371 | {/* Sidebar Open Menu */}
372 | {open
373 | ? ""
374 | : { setOpen(true); }} >
375 |
376 |
377 |
378 |
379 | }
380 |
381 | {/* Add Chat Icon */}
382 | {/* */}
387 |
388 | {/* Sidebar, ChatRoom */}
389 |
390 |
391 | {/* Sidebar */}
392 |
393 |
394 |
395 |
396 |
{ setOpen(false); }} >
397 |
398 |
399 |
400 |
401 |
402 |
403 |
{ profiletoggler(); }} >
404 |
405 |
406 |
{user?.firstname + " " + user?.lastname}
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 | setSearch(e.target.value)} />
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 | {/* Chatroom tiles */}
427 |
428 |
429 |
430 | {chatroomtiles.filter(opt => {
431 | if (search) {
432 | for (let i = 0; i < opt.usernames.length; i++) {
433 | const item = opt.usernames[i];
434 | const fullname = user.firstname + " " + user.lastname;
435 | if (item !== fullname && item.toLowerCase().includes(search.toLowerCase())) {
436 | return true;
437 | }
438 | }
439 | return false;
440 | } else {
441 | return true;
442 | }
443 | }).map((chatroomtile, index) => (
444 |
{
447 | chatRef.current = chatroomtile;
448 | setCurrentchat(chatroomtile);
449 | setOpen(false);
450 | }}
451 | >
452 |
453 |
454 | ))}
455 |
456 |
457 |
458 |
459 | {/* Chatroom */}
460 |
461 | {currentchat ? (
462 | <>
463 |
464 |
465 |

466 |
467 |
470 |
471 | {amigo?.plans.map((plan, index) => (
472 |
473 | {plan.planName}{index < amigo?.plans.length - 1 ? og : ''}
474 |
475 | ))}
476 |
477 |
478 | Sist sett: {amigo && amigo.logAt ? localeFunc(format(amigo.logAt)) : ""}
479 | |
480 | Lokal tid: {amigo && amigo.logAt ? moment(amigo.logAt).format("MMM DD, YYYY, HH:mm") : ""}
481 |
482 |
483 |
484 |
485 | {/*
486 |
487 |
488 |
*/}
489 |
490 |
491 |
492 |
493 |
{ setPick(false) }}>
494 |
495 |
496 |
497 |
498 |
499 |
500 | Du er i trygge hender
501 |
502 |
503 |
504 |
505 |
506 | For økt sikkerhet, hold betalinger og kommunikasjon innenfor Upit. Lær mer
507 |
508 |
509 | {messages.map((message, index) => (
510 |
511 |
512 |
513 | ))}
514 |
515 |
516 |
517 |
518 |
519 |
520 |
527 |
532 |
533 |
546 |
547 |
551 |
552 |
553 |
554 |
555 |
556 |

557 |
{online ? "Aktiv" : "Inaktiv"}
558 |
559 |
Om
560 | {amigo ? amigo?.firstname + " " + amigo?.lastname : ""}
561 |
562 |
563 |
564 | Lokasjon
565 | {amigoDetail?.customFields?.lokasjon}
566 |
567 |
568 | Spesialist
569 | {amigoDetail?.customFields?.jobbkategori}
570 |
571 |
572 | Telefon
573 | {amigoDetail?.customFields?.telefonnummer}
574 |
575 |
576 | Medlem siden
577 | {moment(amigoDetail?.createdAt).format("MMM YYYY")}
578 |
579 |
580 |
581 |
582 |
585 | >
586 | ) : (
587 |
588 | )}
589 |
590 |
591 | {fileModal && (
592 |
599 | Send File
600 |
601 |

602 |
603 | {file?.name}
604 | {filesize(file?.size, { base: 2, standard: "jedec" })}
605 |
606 |
607 | Max file size: 10 MB
608 |
611 |
612 | )}
613 |
614 | );
615 | }
616 |
617 | export default Home;
618 |
--------------------------------------------------------------------------------
/src/Pages/HomeCSS/ChatRoom.css:
--------------------------------------------------------------------------------
1 | .chatroom {
2 | display: flex;
3 | flex-direction: column;
4 | width: 100%;
5 | position: relative;
6 | }
7 |
8 | .chatroom-header {
9 | display: flex;
10 | align-items: end;
11 | justify-content: space-between;
12 | padding: 20px;
13 | border-bottom: 1px solid #eaeaea;
14 | border-top-right-radius: 10px;
15 | z-index: 80;
16 | }
17 |
18 | .chatroom-chatinfo-right {
19 | display: flex;
20 | flex-direction: column;
21 | gap: 3px;
22 | }
23 |
24 | .chatroom-chatinfo {
25 | display: flex;
26 | align-items: center;
27 | }
28 |
29 | .chatroom-chatinfo-name {
30 | font-size: 18px;
31 | font-weight: 500;
32 | color: #333;
33 | }
34 |
35 | .chatroom-chatinfo-name p {
36 | margin: 0;
37 | }
38 |
39 | .chatroom-options {
40 | display: flex;
41 | align-items: center;
42 | }
43 |
44 | .chatroom-container {
45 | display: flex;
46 | flex-direction: row;
47 | height: calc(100% - 99px);
48 | }
49 |
50 | .chatroom-messages-container {
51 | flex: 1;
52 | padding: 20px 0;
53 | overflow-y: scroll;
54 | overflow-x: hidden;
55 | }
56 |
57 | /* Scrollbar */
58 |
59 | .chatroom-messages-container::-webkit-scrollbar-track {
60 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
61 | border-radius: 10px;
62 | background-color: #f5f5f5;
63 | }
64 |
65 | .chatroom-messages-container::-webkit-scrollbar {
66 | width: 8px;
67 | background-color: #f5f5f5;
68 | }
69 |
70 | .chatroom-messages-container::-webkit-scrollbar-thumb {
71 | border-radius: 10px;
72 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
73 | background-color: rgb(0 0 0 / 20%);
74 | }
75 |
76 | .chatroom-profile {
77 | padding: 20px;
78 | width: 300px;
79 | min-width: 300px;
80 | display: flex;
81 | flex-direction: column;
82 | }
83 |
84 | .profile-photo {
85 | aspect-ratio: 1;
86 | border-radius: 15px;
87 | object-fit: cover;
88 | }
89 |
90 | .emoji-picker-open {
91 | position: absolute;
92 | bottom: 8.5%;
93 | z-index: 1000;
94 | }
95 |
96 | .emoji-picker-close {
97 | display: none;
98 | }
99 |
100 | .chatroom-footer {
101 | display: flex;
102 | align-items: center;
103 | gap: 10px;
104 | border-top: 0.1px solid rgb(212, 212, 212);
105 | padding: 10px 15px;
106 | height: fit-content;
107 | box-shadow: 0px -2px 15px rgba(0, 0, 0, 0.07);
108 | padding-top: 20px;
109 | padding-bottom: 20px;
110 | border-top: 0.1px solid #d4d4d478;
111 | }
112 |
113 | .chatroom-plans {
114 | display: flex;
115 | flex-direction: row;
116 | }
117 |
118 | .chatroom-plan {
119 | color: #333;
120 | font-size: 14px;
121 | }
122 |
123 | .chatroom-top-header {
124 | display: flex;
125 | flex-direction: row;
126 | gap: 5px;
127 | color: #74767e;
128 | font-size: 12px;
129 | }
130 |
131 | .chatroom-footer form {
132 | display: flex;
133 | flex-direction: row;
134 | align-items: center;
135 | justify-content: center;
136 | height: fit-content;
137 | width: 100%;
138 | }
139 |
140 | .chatroom-footer form button {
141 | display: none;
142 | }
143 |
144 | .chatroom-safety {
145 | display: flex;
146 | flex-direction: column;
147 | align-items: center;
148 | padding: 10px 20px;
149 | gap: 10px;
150 | }
151 |
152 | .chatroom-safety-header {
153 | display: flex;
154 | align-items: center;
155 | color: #74767e;
156 | font-weight: 500;
157 | font-size: 16px;
158 | gap: 5px;
159 | }
160 |
161 | .chatroom-safety-text {
162 | font-size: 12px;
163 | color: #74767e;
164 | text-align: center;
165 | }
166 |
167 | .message-input {
168 | font-family: "Inter", sans-serif;
169 | padding: 12px 20px;
170 | height: 15px;
171 | outline: none;
172 | border: none;
173 | border-radius: 25px;
174 | font-size: 17px;
175 | background-color: rgb(0 0 0 / 4%);
176 | resize: none;
177 | color: #333;
178 | word-wrap: break-word;
179 | word-break: break-all;
180 | width: 100%;
181 | }
182 |
183 | .message-input::-webkit-scrollbar-track {
184 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
185 | border-radius: 10px;
186 | background-color: #f5f5f5;
187 | }
188 |
189 | .message-input::-webkit-scrollbar {
190 | width: 8px;
191 | background-color: #f5f5f5;
192 | }
193 |
194 | .message-input::-webkit-scrollbar-thumb {
195 | border-radius: 10px;
196 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
197 | background-color: rgb(0 0 0 / 20%);
198 | }
199 |
200 | .input-button {
201 | display: none;
202 | }
203 |
204 | .profile-description {
205 | margin-top: 10px;
206 | display: flex;
207 | flex-direction: column;
208 | }
209 |
210 | .desc-title {
211 | margin-top: 20px;
212 | font-size: 16px;
213 | font-weight: 600;
214 | color: #333;
215 | }
216 |
217 | .desc-text {
218 | font-size: 16px;
219 | padding: 15px 0;
220 | border-bottom: 1px solid #EAEAEA;
221 | }
222 |
223 | .desc-text-value {
224 | font-weight: 600;
225 | color: #333;
226 | text-overflow: ellipsis;
227 | white-space: nowrap;
228 | overflow: hidden;
229 | }
230 |
231 | .desc-profile-name {
232 | color: #333;
233 | }
234 |
235 | .chatroom-search-container {
236 | display: flex;
237 | align-items: center;
238 | border-radius: 15px;
239 | border: 1px solid #EAEAEA;
240 | background: transparent;
241 | width: 100%;
242 | }
243 |
244 | .chatroom-searchicon {
245 | margin-left: 15px;
246 | color: #333;
247 | }
248 |
249 | .chatroom-search-container input {
250 | border: none;
251 | outline: none;
252 | height: 30px;
253 | border-radius: 15px;
254 | padding: 5px 15px;
255 | font-size: 16px;
256 | display: flex;
257 | width: 100%;
258 | margin-right: 25px;
259 | background-color: transparent;
260 | }
261 |
262 | .chatroom-footer-lefticons {
263 | display: flex;
264 | gap: 10px;
265 | }
266 | .icon-picker {
267 | display: block;
268 | }
269 |
270 | .send-icon {
271 | color: #316af3;
272 | }
273 |
274 | @media screen and (max-width:1200px) {
275 | .chatroom-profile {
276 | display: none;
277 | }
278 | .chatroom-search {
279 | display: none;
280 | }
281 | }
282 |
283 | @media screen and (max-width: 768px) {
284 | .chatroom-footer {
285 | padding: 10px 5px;
286 | }
287 | .chatroom-search {
288 | display: block;
289 | }
290 | .chatroom-footer-lefticons {
291 | display: flex;
292 | }
293 | .icon-picker {
294 | display: none !important;
295 | }
296 | .chatroom {
297 | flex: 1;
298 | }
299 | .chatroom-top-header span:nth-child(2), .chatroom-top-header span:nth-child(3) {
300 | display: none;
301 | }
302 | .chatroom-footer-righticon button{
303 | width: 48px;
304 | height: 48px;
305 | min-width: 48px;
306 | padding: 0 !important;
307 | }
308 | .chatroom-footer-righticon .send-text {
309 | display: none;
310 | }
311 | .chatroom-chatinfo {
312 | margin-left: 30px;
313 | }
314 | }
315 |
316 | @media screen and (max-width: 576px) {
317 | .chatroom-search {
318 | display: none;
319 | }
320 | }
--------------------------------------------------------------------------------
/src/Pages/HomeCSS/Home.css:
--------------------------------------------------------------------------------
1 | .home {
2 | display: grid;
3 | place-items: center;
4 | height: 100vh;
5 | background-color: #0e1012;
6 | position: relative;
7 | padding: 0 50px;
8 | }
9 |
10 | .home-components {
11 | display: flex;
12 | height: 95vh;
13 | width: 100%;
14 | margin: auto;
15 | background-color: #fff;
16 | border-radius: 10px;
17 | -webkit-box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
18 | 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);
19 | box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
20 | 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);
21 | }
22 |
23 | .menu-open{
24 | position: absolute;
25 | z-index: 1000;
26 | top: 26px;
27 | left: -1px;
28 | border-radius: 50%;
29 | }
30 |
31 | .menu-close{
32 | z-index: 1000;
33 | border-radius: 50%;
34 | }
35 |
36 | .add-chatroom-icon {
37 | position: absolute;
38 | top: 6%;
39 | left: 85%;
40 | z-index: 2000;
41 | }
42 |
43 | @media screen and (min-width: 768px) {
44 | .menu-close {
45 | display: none;
46 | }
47 | .menu-open {
48 | display: none;
49 | }
50 | }
51 |
52 | @media screen and (max-width: 768px) {
53 | .home {
54 | overflow: hidden;
55 | padding: 0;
56 | }
57 | .home-components {
58 | height: 100vh;
59 | width: 100vw;
60 | border-radius: 0;
61 | }
62 | .add-chatroom-icon {
63 | position: absolute;
64 | top: 1%;
65 | left: 85%;
66 | }
67 | }
68 |
69 | .btn-send {
70 | background: #12A4FF !important;
71 | border-radius: 30px !important;
72 | padding: 12px 20px !important;
73 | color: white !important;
74 | font-size: 16px;
75 | }
76 |
77 | .btn-send-file {
78 | background: #fff !important;
79 | border-radius: 30px !important;
80 | padding: 12px 20px !important;
81 | color: white !important;
82 | font-size: 16px;
83 | color: #000 !important;
84 | }
85 |
86 | .sidebar-members {
87 | width: 100%;
88 | height: 100%;
89 | }
90 |
91 | .ScrollbarsCustom-Wrapper {
92 | inset: 0 !important;
93 | }
94 |
95 | .ScrollbarsCustom-Track {
96 | width: 8px !important;
97 | }
98 |
99 | .ScrollbarsCustom-Thumb {
100 | background: rgb(0 0 0 / 20%) !important;
101 | }
102 |
103 | .file-upload {
104 | display: none;
105 | }
106 |
107 | .ReactModal__Overlay {
108 | opacity: 0;
109 | transform: translateY(-100px);
110 | transition: all 100ms ease-in-out;
111 | background-color: rgb(255 255 255 / 0%) !important;
112 | z-index: 99;
113 | }
114 |
115 | .ReactModal__Overlay--after-open {
116 | opacity: 1;
117 | transform: translateY(0px);
118 | }
119 |
120 | .ReactModal__Overlay--before-close {
121 | opacity: 0;
122 | transform: translateY(-100px);
123 | }
124 |
125 | .ReactModal__Content {
126 | background: #12a4ff !important;
127 | border: none !important;
128 | border-radius: 10px !important;
129 | max-width: 400px;
130 | width: 100%;
131 | height: fit-content;
132 | top: 0;
133 | bottom: 0;
134 | left: 0;
135 | right: 0;
136 | margin: auto;
137 | display: flex;
138 | flex-direction: column;
139 | gap: 10px;
140 | }
141 |
142 | .modal-title {
143 | color: white;
144 | }
145 |
146 | .modal-file-img {
147 | width: 40px;
148 | height: 40px;
149 | background: white;
150 | border-radius: 50%;
151 | padding: 10px;
152 | }
153 |
154 | .modal-file-name {
155 | font-size: 20px;
156 | color: white;
157 | width: 300px;
158 | overflow: hidden;
159 | text-overflow:ellipsis;
160 | white-space: nowrap;
161 | }
162 |
163 | .modal-file-size {
164 | font-size: 16px;
165 | color: white;
166 | }
167 |
168 | .modal-max-file {
169 | color: white;
170 | }
--------------------------------------------------------------------------------
/src/Pages/HomeCSS/Sidebar.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | display: flex;
3 | flex-direction: column;
4 | border-right: 1px solid #EAEAEA;
5 | width: 350px;
6 | min-width: 350px;
7 | }
8 |
9 | .sidebar-top-header {
10 | border-bottom: 1px solid #EAEAEA;
11 | }
12 |
13 | .sidebar-mobile-header {
14 | display: none;
15 | }
16 |
17 | .sidebar-header {
18 | display: flex;
19 | justify-content: space-between;
20 | padding: 10px 15px;
21 | align-items: center;
22 | position: relative;
23 | }
24 |
25 | .sidebar-search {
26 | background-color: #fff;
27 | display: flex;
28 | align-items: center;
29 | justify-content: flex-start;
30 | height: 50px;
31 | padding: 10px;
32 | margin-bottom: 10px;
33 | }
34 |
35 | .sidebar-searchicon {
36 | margin-left: 15px;
37 | }
38 |
39 | .sidebar-search-container {
40 | display: flex;
41 | align-items: center;
42 | border-radius: 15px;
43 | background-color: #f5f5f5;
44 | width: 100%;
45 | }
46 |
47 | .sidebar-search-container input {
48 | border: none;
49 | outline: none;
50 | height: 40px;
51 | border-radius: 15px;
52 | padding: 5px 15px;
53 | font-size: 16px;
54 | display: flex;
55 | width: 100%;
56 | margin-right: 25px;
57 | background-color: #f5f5f5;
58 | }
59 |
60 | .user-profile {
61 | width: 50px !important;
62 | }
63 |
64 | .user-profile .MuiIconButton-label {
65 | width: 50px !important;
66 | }
67 |
68 | .user-profile-image {
69 | height: 50px;
70 | width: 50px;
71 | object-fit: cover;
72 | border-radius: 50%;
73 | position: absolute;
74 | }
75 |
76 | .logout-mobile-option {
77 | display: none;
78 | }
79 |
80 | .sidebar-mobile-profile-name {
81 | display: none;
82 | }
83 |
84 | @media screen and (max-width: 768px) {
85 | .sidebar {
86 | display: none;
87 | }
88 | .sidebar-header {
89 | justify-content: start;
90 | }
91 | .sidebar-mobile-profile-name {
92 | display: block;
93 | font-size: 18px;
94 | font-weight: 400;
95 | margin-left: 10px;
96 | }
97 | .sidebar-mobile-header {
98 | display: flex;
99 | justify-content: space-between;
100 | padding-left: 15px;
101 | padding-right: 6px;
102 | }
103 | .sidebar.active {
104 | position: relative;
105 | width: auto;
106 | height: 100vh;
107 | width: 100vw;
108 | display: flex;
109 | position: fixed;
110 | left: 0;
111 | opacity: 1;
112 | transition: all 0.5s ease;
113 | background-color: #fff;
114 | z-index: 100;
115 | }
116 | .user-profile-image {
117 | position: absolute;
118 | }
119 | .logout-option {
120 | display: none;
121 | }
122 | .logout-mobile-option {
123 | display: block;
124 | margin-left: 10px;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Pages/Signin-up.css:
--------------------------------------------------------------------------------
1 | .signin-container,.signup-container {
2 | height: 100vh;
3 | width: 100vw;
4 | background-color: #316af3;
5 | }
6 |
7 | a {
8 | text-decoration: none;
9 | }
10 |
11 | .signin-card, .signup-card{
12 | position: absolute;
13 | left: 50%;
14 | top: 50%;
15 | transform: translate(-50%, -50%);
16 | font-family: sans-serif;
17 | width: 100%;
18 | max-width: 350px;
19 | margin-left: auto;
20 | margin-right: auto;
21 | margin-top: 38px;
22 | margin-bottom: 38px;
23 | border-radius: 10px !important;
24 | background-color: #ffff;
25 | box-shadow: 2px 5px 20px rgba(0, 0, 0, 0.1);
26 | }
27 |
28 | form {
29 | /* padding: 30px; */
30 | }
31 |
32 | .signin-title, .signup-title{
33 | text-align: center;
34 | font-weight: bold;
35 | margin: 0;
36 | }
37 |
38 | .signup-option, .signup-option {
39 | background-color: rgb(69, 69, 185, 0.2);
40 | border-bottom-left-radius: 10px;
41 | border-bottom-right-radius: 10px;
42 | }
43 |
44 | .line {
45 | text-align: center;
46 | font-weight: bold;
47 | border-bottom: 2px solid rgb(245 239 239);
48 | line-height: 2px;
49 | margin: 25px 0;
50 | }
51 |
52 | .error-message{
53 | color: red;
54 | font-weight: 600;
55 | }
56 |
57 | .signin-fields, .signup-fields{
58 | display: flex;
59 | flex-direction: column;
60 | }
61 |
62 | .signin-fields label, .signup-fields label {
63 | color: rgb(170 166 166);
64 | text-align: left;
65 | margin-top: 15px;
66 | }
67 |
68 | .signin-textbox, .signup-textbox{
69 | padding: 15px 20px;
70 | margin-top: 5px;
71 | margin-bottom: 5px;
72 | border: 1px solid #ccc;
73 | border-radius: 8px;
74 | box-sizing: border-box;
75 | outline: none;
76 | font-size: 20px;
77 | }
78 |
79 | .signin-button,.signup-button {
80 | background-color: rgb(69, 69, 185);
81 | color: white;
82 | padding: 18px 20px;
83 | margin-top: 25px;
84 | margin-left: auto !important;
85 | margin-left: auto !important;
86 | width: 100%;
87 | border-radius: 10px;
88 | border: none;
89 | display: flex;
90 | align-items: center;
91 | justify-content: center;
92 | cursor: pointer;
93 | font-size: 18px;
94 | font-weight: 600;
95 | }
96 |
97 | .forget-pass {
98 | text-align: center;
99 | display: block;
100 | }
101 |
102 | .signup-question {
103 | text-align: center;
104 | font-weight: bold;
105 | margin-top: 15px !important;
106 | padding: 15px;
107 | }
108 |
109 |
110 | /*input file*/
111 | .file-input {
112 | color: transparent;
113 | margin-bottom:0!important;
114 | }
115 | .file-input::-webkit-file-upload-button {
116 | visibility: hidden;
117 | }
118 |
119 | .file-input::before {
120 | content: "Upload Profile Pic";
121 | color: #fff;
122 | display: inline-block;
123 | background: #fa0261;
124 | padding: 10px 22px;
125 | outline: none;
126 | width: fit-content;
127 | margin: 10px auto 10px auto;
128 | -webkit-user-select: none;
129 | cursor: pointer;
130 | font-weight: 600;
131 | border-radius: 2px;
132 | outline: none;
133 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12),
134 | 0 3px 1px -2px rgba(0, 0, 0, 0.2);
135 | }
136 | .file-input:focus {
137 | outline: none !important;
138 | }
139 |
140 | .file-input:active::before {
141 | transform: scale(0.9) translate(0px, 2px);
142 | box-shadow: inset 4px 4px 5px 0px rgba(0, 0, 0, 0.2);
143 | }
144 |
145 | .btn {
146 | width: 120px;
147 | font-size: 16px;
148 | font-weight: 600;
149 | padding: 10px 20px;
150 | border-radius: 5px;
151 | border: none;
152 | }
153 |
154 | .signin-container {
155 | display: flex;
156 | justify-content: center;
157 | align-items: center;
158 | overflow: hidden;
159 | }
160 |
161 | .signin-content {
162 |
163 | }
164 |
165 | .btn-group {
166 | display: flex;
167 | gap: 20px;
168 | }
169 |
170 | .signin-content h1 {
171 | color: white;
172 | font-size: 40px;
173 | margin-bottom: 20px;
174 | text-align: center;
175 | }
176 |
177 | .signin-content h3 {
178 | color: white;
179 | margin-top: 20px;
180 | text-align: center;
181 | }
182 |
183 | footer {
184 | position:absolute;
185 | bottom: 0;
186 | margin-bottom: 10px;
187 | display: flex;
188 | gap: 10px;
189 | }
190 |
191 | footer span {
192 | color: white;
193 | }
--------------------------------------------------------------------------------
/src/Pages/Signin.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useContext } from 'react'
2 | import './Signin-up.css'
3 | import axios from 'axios'
4 | import { Comment } from 'react-loader-spinner';
5 | import { AuthContext } from '../Context/AuthContext'
6 |
7 | function Signin() {
8 | const { dispatch, setLoading, setCurrentMem } = useContext(AuthContext);
9 | const API_URL = process.env.REACT_APP_API_URL;
10 | const API_KEY = process.env.REACT_APP_MEMBERSTACK_KEY;
11 | const BASE_URL = 'https://admin.memberstack.com/members';
12 | const headers = { "X-API-KEY": API_KEY };
13 |
14 | useEffect(() => {
15 | (() => {
16 | try {
17 | // const token = window.location.pathname.replace('/', '');
18 | const token = window.location.search.replace('?', '');
19 | const param = JSON.parse(atob(token));
20 | loginCall(param, dispatch);
21 | } catch (err) {
22 | console.log(err);
23 | }
24 | })()
25 | document.title = "Upit Chat";
26 | }, []);
27 |
28 | const getUserInfo = async (mem_id) => {
29 | try {
30 | const resp = await axios.get(`${BASE_URL}/${mem_id}`, { headers });
31 | return resp.data.data;
32 | } catch (err) {
33 | console.log(err);
34 | }
35 | return null;
36 | }
37 |
38 | const registerCall = async (member_id) => {
39 | try {
40 | const profile = await getUserInfo(member_id);
41 | const res = await axios.post(API_URL + "api/auth/signin", {
42 | firstname: profile.customFields.fornavn,
43 | lastname: profile.customFields.etternavn,
44 | email: profile.auth.email,
45 | mem_id: profile.id,
46 | avatar: profile.customFields.profilbilde,
47 | plans: profile.planConnections
48 | });
49 | }
50 | catch (err) {
51 | console.log("Register Err:", err)
52 | }
53 | }
54 |
55 | const loginCall = async (param, dispatch) => {
56 | dispatch({ type: "LOGIN_START" });
57 | try {
58 | const userInfo = {
59 | email: param.email,
60 | mem_id: param.mem_id
61 | }
62 |
63 | const profile = await getUserInfo(userInfo.mem_id);
64 | const res = await axios.post(API_URL + "api/auth/signin", {
65 | firstname: profile.customFields.fornavn,
66 | lastname: profile.customFields.etternavn,
67 | email: profile.auth.email,
68 | mem_id: profile.id,
69 | avatar: profile.customFields.profilbilde,
70 | plans: profile.planConnections
71 | });
72 |
73 | if (param?.to) {
74 | await registerCall(param.to);
75 | }
76 | await login(res.data._id);
77 |
78 | dispatch({ type: "LOGIN_SUCCESS", payload: res.data });
79 | setLoading(false);
80 | setCurrentMem(param.to);
81 | }
82 | catch (err) {
83 | console.log("Login Err:", err)
84 | dispatch({ type: "LOGIN_FAILURE", payload: err })
85 | }
86 | }
87 |
88 | const login = async (id) => {
89 | try {
90 | await axios.put(API_URL + "api/auth/" + id);
91 | }
92 | catch (err) {
93 | console.log(err)
94 | }
95 | }
96 |
97 | return (
98 |
112 | )
113 | }
114 |
115 | export default Signin
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800&display=swap");
2 |
3 | * {
4 | margin: 0;
5 | }
6 |
7 | body {
8 | margin: 0;
9 | font-family: "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
10 | "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
11 | -webkit-font-smoothing: antialiased;
12 | -moz-osx-font-smoothing: grayscale;
13 | }
14 |
15 | code {
16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
17 | monospace;
18 | }
19 |
20 | .flex {
21 | display: flex;
22 | }
23 |
24 | .flex-row {
25 | flex-direction: row;
26 | }
27 |
28 | .flex-col {
29 | flex-direction: column;
30 | }
31 |
32 | .justify-center {
33 | justify-content: center;
34 | }
35 |
36 | .justify-between {
37 | justify-content: space-between;
38 | }
39 |
40 | .justify-end {
41 | justify-content: flex-end;
42 | }
43 |
44 | .justify-evenly {
45 | justify-content: space-evenly;
46 | }
47 |
48 | .align-items-center {
49 | align-items: center;
50 | }
51 |
52 | .flex-row-between {
53 | display: flex;
54 | justify-content: space-between;
55 | }
56 |
57 | .gap-5 {
58 | gap: 5px;
59 | }
60 |
61 | .gap-10 {
62 | gap: 10px;
63 | }
64 |
65 | .gap-15 {
66 | gap: 15px;
67 | }
68 |
69 | .gap-20 {
70 | gap: 20px;
71 | }
72 |
73 | .fw-400 {
74 | font-weight: 400;
75 | }
76 |
77 | .fw-500 {
78 | font-weight: 500;
79 | }
80 |
81 | .fw-600 {
82 | font-weight: 600;
83 | }
84 |
85 | .gray-primary {
86 | color: #74767e;
87 | }
88 |
89 | .black-primary {
90 | color: #333;
91 | }
92 |
93 | .fs-14 {
94 | font-size: 14px;
95 | }
96 |
97 | .fs-15 {
98 | font-size: 15px;
99 | }
100 |
101 | .fs-16 {
102 | font-size: 16px;
103 | }
104 |
105 | .pl-5 {
106 | padding-left: 5px;
107 | }
108 |
109 | .ml-5 {
110 | margin-left: 5px;
111 | }
112 |
113 | .MuiIconButton-root {
114 | background: rgb(0 0 0 / 4%);
115 | }
116 |
117 | .w-100 {
118 | width: 100%;
119 | }
120 |
121 | .h-100 {
122 | height: 100%;
123 | }
124 |
125 | .safety-line {
126 | flex: 1;
127 | height: 1px;
128 | background: #eaeaea;
129 | }
130 |
131 | .MuiButton-label {
132 | font-size: 15px;
133 | font-family: "Inter";
134 | font-weight: 700;
135 | line-height: 1;
136 | }
137 |
138 | .text-underline {
139 | text-decoration-line: underline;
140 | }
141 |
142 | .m-0 {
143 | margin: 0 !important;
144 | }
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import { AuthContextProvider } from "./Context/AuthContext"
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------