Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4492d54

Browse files
committedSep 22, 2023
first commit
0 parents  commit 4492d54

23 files changed

+4400
-0
lines changed
 

‎.eslintrc.cjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
env: { browser: true, es2020: true },
3+
extends: [
4+
'eslint:recommended',
5+
'plugin:react/recommended',
6+
'plugin:react/jsx-runtime',
7+
'plugin:react-hooks/recommended',
8+
],
9+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
10+
settings: { react: { version: '18.2' } },
11+
plugins: ['react-refresh'],
12+
rules: {
13+
'react-refresh/only-export-components': 'warn',
14+
},
15+
}

‎.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
.env
10+
11+
node_modules
12+
dist
13+
dist-ssr
14+
*.local
15+
16+
# Editor directories and files
17+
.vscode/*
18+
!.vscode/extensions.json
19+
.idea
20+
.DS_Store
21+
*.suo
22+
*.ntvs*
23+
*.njsproj
24+
*.sln
25+
*.sw?

‎images/demo.png

262 KB
Loading

‎images/login.png

77.1 KB
Loading

‎images/room.png

171 KB
Loading

‎index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.jsx"></script>
12+
</body>
13+
</html>

‎package-lock.json

Lines changed: 3580 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "chat1",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "vite build",
9+
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"appwrite": "^11.0.0",
14+
"react": "^18.2.0",
15+
"react-dom": "^18.2.0",
16+
"react-feather": "^2.0.10",
17+
"react-router-dom": "^6.13.0"
18+
},
19+
"devDependencies": {
20+
"@types/react": "^18.0.37",
21+
"@types/react-dom": "^18.0.11",
22+
"@vitejs/plugin-react": "^4.0.0",
23+
"eslint": "^8.38.0",
24+
"eslint-plugin-react": "^7.32.2",
25+
"eslint-plugin-react-hooks": "^4.6.0",
26+
"eslint-plugin-react-refresh": "^0.3.4",
27+
"vite": "^4.3.9"
28+
}
29+
}

‎public/vite.svg

Lines changed: 1 addition & 0 deletions
Loading

‎readme.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Real Time Chat App With React JS and Appwrite
2+
3+
A chat app with real-time capabilities that utilizes Appwrite on the backend.
4+
5+
> Tutorial Link Will be added here when posted.
6+
7+
<img src="images/demo.png"/>
8+
9+
### Getting Started
10+
11+
After cloning the repo ensure you complete the necessary installations
12+
13+
```
14+
$ npm install
15+
$ npm run dev
16+
```
17+
18+
Create a new `.env` folder and create the necessary variables based on the `src/appwriteConfig.js` file. Appwrite setup will be covered in the next step.
19+
20+
```js
21+
//appwrite.Config.js
22+
...
23+
export const API_ENDPOINT = import.meta.env.VITE_API_ENDPOINT
24+
export const PROJECT_ID = import.meta.env.VITE_PROJECT_ID
25+
export const DATABASE_ID = import.meta.env.VITE_DATABASE_ID
26+
export const COLLECTION_ID_MESSAGES = import.meta.env.VITE_COLLECTION_ID_MESSAGES
27+
28+
const client = new Client()
29+
.setEndpoint(API_ENDPOINT)
30+
.setProject(PROJECT_ID);
31+
...
32+
```
33+
34+
**Setting Up Appwrite Account**
35+
36+
Set up a local instance of Appwrite or create an account with Appwrite Cloud.
37+
38+
In your appwrite console create a project and database.
39+
40+
1. Create a collection called "messages" and add the following attributes:
41+
- user_id
42+
- username
43+
- body
44+
2. From your `messages` collection, go to the "settings" --> "Update Permissions" --> "+ Add Role" and select "Any". Give this user type "Create", "Read", "Update" and "Delete" permissions.
45+
46+
Once you've set up your project you should be able to update all necessary env variables.
47+
Run your development server to view the output.

‎src/App.css

Whitespace-only changes.

‎src/App.jsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import './App.css'
2+
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'
3+
4+
import PrivateRoutes from './utils/PrivateRoutes'
5+
import Room from './pages/room'
6+
import LoginPage from './pages/LoginPage'
7+
import RegisterPage from './pages/RegisterPage'
8+
import { AuthProvider } from './utils/AuthContext'
9+
10+
11+
12+
function App() {
13+
14+
15+
return (
16+
17+
<Router>
18+
19+
<AuthProvider>
20+
21+
<Routes>
22+
<Route path="/login" element={<LoginPage/>}/>
23+
<Route path="/register" element={<RegisterPage/>}/>
24+
<Route element={<PrivateRoutes/>}>
25+
<Route path="/" element={<Room/>}/>
26+
</Route>
27+
</Routes>
28+
</AuthProvider>
29+
</Router>
30+
31+
)
32+
}
33+
34+
export default App

‎src/appwriteConfig.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Client, Account, Databases } from 'appwrite';
2+
3+
export const API_ENDPOINT = import.meta.env.VITE_API_ENDPOINT
4+
export const PROJECT_ID = import.meta.env.VITE_PROJECT_ID
5+
export const DATABASE_ID = import.meta.env.VITE_DATABASE_ID
6+
export const COLLECTION_ID_MESSAGES = import.meta.env.VITE_COLLECTION_ID_MESSAGES
7+
8+
const client = new Client()
9+
.setEndpoint(API_ENDPOINT)
10+
.setProject(PROJECT_ID);
11+
12+
export const account = new Account(client);
13+
export const databases = new Databases(client)
14+
15+
export default client;

‎src/assets/react.svg

Lines changed: 1 addition & 0 deletions
Loading

‎src/components/Header.jsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react'
2+
import { useAuth } from '../utils/AuthContext'
3+
import { Link } from 'react-router-dom'
4+
import { LogOut, LogIn } from 'react-feather'
5+
6+
const Header = () => {
7+
const {user, handleLogout} = useAuth()
8+
return (
9+
<div id="header--wrapper">
10+
{user ? (
11+
<>
12+
Welcome {user.name}
13+
<LogOut className="header--link" onClick={handleLogout}/>
14+
</>
15+
): (
16+
<>
17+
<Link to="/">
18+
<LogIn className="header--link"/>
19+
</Link>
20+
</>
21+
)}
22+
</div>
23+
)
24+
}
25+
26+
export default Header

‎src/index.css

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/* Global settings */
2+
3+
:root{
4+
--mainBgColor:rgba(20,20,31,11);
5+
--secondaryBgColor:rgba(27,27,39,1);
6+
--borderColor1:rgba(40,41,57,1);
7+
--borderColor2:rgba(79,86,105,1);
8+
--textColorMain:#fff;
9+
--textColorSecondary:rgb(226, 227, 232);
10+
--themeColorMain:rgba(219,26,90,1);
11+
--themeColorSecondary:#386fd2;
12+
}
13+
14+
* {
15+
margin: 0;
16+
padding: 0;
17+
box-sizing: border-box;
18+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
19+
'Open Sans', 'Helvetica Neue', sans-serif;
20+
}
21+
22+
a{
23+
color: #40e0d0;
24+
}
25+
26+
body{
27+
background-color: var(--mainBgColor);
28+
color: var(--textColorMain);
29+
}
30+
31+
.container{
32+
max-width: 600px;
33+
margin:20px auto;
34+
}
35+
36+
/* Login & Register Pages */
37+
38+
.auth--container{
39+
height: 100vh;
40+
display: flex;
41+
align-items: center;
42+
justify-content: center;
43+
}
44+
45+
.form--wrapper{
46+
width: 600px;
47+
width: 600px;
48+
padding: 2em;
49+
}
50+
51+
.field--wrapper{
52+
display: flex;
53+
flex-direction: column;
54+
gap: 1em;
55+
padding: 0.5em 0;
56+
}
57+
58+
59+
60+
/* Form Input Styling */
61+
62+
input[type="text"], input[type="password"], input[type="email"], textarea{
63+
background: var(--mainBgColor);
64+
border: none;
65+
border-bottom: 1px solid var(--borderColor1);
66+
padding: 1rem;
67+
border-radius: 3px;
68+
width: 100%;
69+
color: var(--color-light);
70+
outline: none;
71+
font-size: 18px;
72+
text-shadow: none !important;
73+
}
74+
75+
76+
/* Button Styling */
77+
78+
.btn{
79+
padding: 0.5em 1em;
80+
border: none;
81+
border-radius: 2px;
82+
cursor: pointer;
83+
transition: 0.3s;
84+
}
85+
86+
.btn--lg{
87+
padding: 1em 2em;
88+
}
89+
90+
.btn--main{
91+
background-color: rgba(219,26,90,1);
92+
color: #fff;
93+
}
94+
95+
.btn--secondary{
96+
background-color: #8db3dd;
97+
}
98+
99+
.btn:hover{
100+
opacity: 0.7;
101+
}
102+
103+
/* Header Styling */
104+
105+
#header--wrapper{
106+
background-color: var(--mainBgColor);
107+
padding: 1em;
108+
display: flex;
109+
justify-content: space-between;
110+
border-radius: 10px 10px 0 0;
111+
border:1px solid var(--borderColor1);
112+
border-bottom: none;
113+
}
114+
115+
.header--link{
116+
color: #c7d8eb;
117+
cursor: pointer;
118+
transition: 0.3s;
119+
}
120+
121+
.header--link:hover{
122+
color: #8db3dd;
123+
}
124+
125+
/* Room Styling */
126+
127+
.room--container{
128+
padding: 2em;
129+
background-color: var(--secondaryBgColor);
130+
border-radius: 0 0 10px 10px;
131+
border:1px solid var(--borderColor1);
132+
}
133+
134+
#message--form{
135+
display: flex;
136+
flex-direction: column;
137+
gap: 0.5em;
138+
}
139+
140+
.send-btn--wrapper{
141+
display: flex;
142+
justify-content: flex-end;
143+
}
144+
145+
.message--wrapper{
146+
display: flex;
147+
flex-wrap: wrap;
148+
flex-direction: column;
149+
gap: 0.5em;
150+
margin:1em;
151+
}
152+
153+
.message--header{
154+
display: flex;
155+
justify-content: space-between;
156+
align-items: center;
157+
}
158+
159+
.message--body{
160+
padding: 0.5em 0;
161+
color: var(--textColorSecondary);
162+
background-color: var(--themeColorMain);
163+
padding: 1em;
164+
border-radius: 20px;
165+
width:fit-content;
166+
max-width: 100%;
167+
word-wrap: break-word; /* added */
168+
}
169+
170+
.message--body--owner{
171+
border: 1px solid rgba(219,26,90,1);
172+
background-color: var(--secondaryBgColor);
173+
}
174+
175+
.message-timestamp{
176+
margin-left: 1em;
177+
color: rgb(164, 161, 161);
178+
}
179+
180+
.delete--btn{
181+
color:#8db3dd;
182+
cursor: pointer;
183+
transition: 0.3s;
184+
width: 16px;
185+
}
186+
187+
.delete--btn:hover{
188+
color:red;
189+
}
190+
191+
192+
193+
194+

‎src/main.jsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom/client'
3+
import App from './App.jsx'
4+
import './index.css'
5+
6+
ReactDOM.createRoot(document.getElementById('root')).render(
7+
<React.StrictMode>
8+
<App />
9+
</React.StrictMode>,
10+
)

‎src/pages/LoginPage.jsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React, {useEffect, useState} from 'react'
2+
import { useAuth } from '../utils/AuthContext'
3+
import { useNavigate } from 'react-router'
4+
import { Link } from 'react-router-dom'
5+
import '../index.css'
6+
7+
8+
const LoginPage = () => {
9+
const {user, handleUserLogin} = useAuth()
10+
const [credentials, setCredentials] = useState({email:"", password:""})
11+
12+
const navigate = useNavigate()
13+
14+
useEffect(() => {
15+
if (user){
16+
navigate('/')
17+
}
18+
}, [])
19+
20+
const handleInputChange = (e) => {
21+
let name = e.target.name
22+
let value = e.target.value
23+
24+
setCredentials({...credentials, [name]:value})
25+
// console.log('CREDS:', credentials)
26+
}
27+
28+
return (
29+
30+
<div className="auth--container">
31+
<div className="form--wrapper">
32+
<form onSubmit={(e) => {handleUserLogin(e, credentials)}}>
33+
<div className="field--wrapper">
34+
<label>Email:</label>
35+
<input
36+
required
37+
type="email"
38+
name="email"
39+
placeholder="Enter your email..."
40+
value={credentials.email}
41+
onChange={(e) => {handleInputChange(e)}}
42+
/>
43+
</div>
44+
45+
<div className="field--wrapper">
46+
<label>Password:</label>
47+
<input
48+
required
49+
type="password"
50+
name="password"
51+
placeholder="Enter password..."
52+
value={credentials.password}
53+
onChange={(e) => {handleInputChange(e)}}
54+
/>
55+
</div>
56+
57+
<div className="field--wrapper">
58+
59+
<input
60+
type="submit"
61+
value="Login"
62+
className="btn btn--lg btn--main"
63+
/>
64+
65+
</div>
66+
</form>
67+
68+
<p>Dont have an account? Register <Link to="/register">here</Link></p>
69+
</div>
70+
</div>
71+
72+
)
73+
}
74+
75+
export default LoginPage

‎src/pages/RegisterPage.jsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react'
2+
import {useState} from 'react'
3+
import { useAuth } from '../utils/AuthContext'
4+
import { Link } from 'react-router-dom'
5+
6+
const RegisterPage = () => {
7+
8+
const [credentials, setCredentials] = useState({name:'',email:'', password1:'', password2:''})
9+
10+
const {handleRegister} = useAuth()
11+
12+
13+
14+
const handleInputChange = (e) => {
15+
let name = e.target.name
16+
let value = e.target.value
17+
18+
setCredentials({...credentials, [name]:value})
19+
// console.log('CREDS:', credentials)
20+
}
21+
22+
return (
23+
<div className="auth--container">
24+
<div className="form--wrapper">
25+
26+
<form onSubmit={(e) => {handleRegister(e, credentials)}}>
27+
<div className="field--wrapper">
28+
<label>Name:</label>
29+
<input
30+
required
31+
type="text"
32+
name="name"
33+
value={credentials.name}
34+
placeholder="Enter your name..."
35+
onChange={(e) => {handleInputChange(e)}}
36+
/>
37+
</div>
38+
39+
<div className="field--wrapper">
40+
<label>Email:</label>
41+
<input
42+
required
43+
type="email"
44+
name="email"
45+
placeholder="Enter your email..."
46+
value={credentials.email}
47+
onChange={(e) => {handleInputChange(e)}}
48+
/>
49+
</div>
50+
51+
<div className="field--wrapper">
52+
<label>Password:</label>
53+
<input
54+
required
55+
type="password"
56+
name="password1"
57+
placeholder="Enter a password..."
58+
value={credentials.password1}
59+
onChange={(e) => {handleInputChange(e)}}
60+
/>
61+
</div>
62+
63+
<div className="field--wrapper">
64+
<label>Confirm password:</label>
65+
<input
66+
required
67+
type="password"
68+
name="password2"
69+
placeholder="Comfirm your password..."
70+
value={credentials.password2}
71+
onChange={(e) => {handleInputChange(e)}}
72+
/>
73+
</div>
74+
75+
<div className="field--wrapper">
76+
<input className="btn btn--lg btn--main" type="submit" value="Register"/>
77+
</div>
78+
</form>
79+
80+
<p>Already have an account? Login <Link to="/login">here</Link></p>
81+
</div>
82+
</div>
83+
)
84+
}
85+
86+
export default RegisterPage

‎src/pages/Room.jsx

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React, {useState, useEffect} from 'react'
2+
import client, { databases, DATABASE_ID, COLLECTION_ID_MESSAGES } from '../appwriteConfig'
3+
import { ID, Query, Permission, Role} from 'appwrite';
4+
import Header from '../components/Header';
5+
import { useAuth } from '../utils/AuthContext';
6+
import {Trash2} from 'react-feather'
7+
8+
9+
const Room = () => {
10+
const [messageBody, setMessageBody] = useState('')
11+
const [messages, setMessages] = useState([])
12+
const {user} = useAuth()
13+
14+
15+
useEffect(() => {
16+
getMessages();
17+
18+
const unsubscribe = client.subscribe(`databases.${DATABASE_ID}.collections.${COLLECTION_ID_MESSAGES}.documents`, response => {
19+
20+
if(response.events.includes("databases.*.collections.*.documents.*.create")){
21+
console.log('A MESSAGE WAS CREATED')
22+
setMessages(prevState => [response.payload, ...prevState])
23+
}
24+
25+
if(response.events.includes("databases.*.collections.*.documents.*.delete")){
26+
console.log('A MESSAGE WAS DELETED!!!')
27+
setMessages(prevState => prevState.filter(message => message.$id !== response.payload.$id))
28+
}
29+
});
30+
31+
console.log('unsubscribe:', unsubscribe)
32+
33+
return () => {
34+
unsubscribe();
35+
};
36+
}, []);
37+
38+
39+
const getMessages = async () => {
40+
const response = await databases.listDocuments(
41+
DATABASE_ID,
42+
COLLECTION_ID_MESSAGES,
43+
[
44+
Query.orderDesc('$createdAt'),
45+
Query.limit(100),
46+
]
47+
)
48+
console.log(response.documents)
49+
setMessages(response.documents)
50+
}
51+
52+
const handleSubmit = async (e) => {
53+
e.preventDefault()
54+
console.log('MESSAGE:', messageBody)
55+
56+
const permissions = [
57+
Permission.write(Role.user(user.$id)),
58+
]
59+
60+
const payload = {
61+
user_id:user.$id,
62+
username:user.name,
63+
body:messageBody
64+
}
65+
66+
const response = await databases.createDocument(
67+
DATABASE_ID,
68+
COLLECTION_ID_MESSAGES,
69+
ID.unique(),
70+
payload,
71+
permissions
72+
)
73+
74+
console.log('RESPONSE:', response)
75+
76+
// setMessages(prevState => [response, ...prevState])
77+
78+
setMessageBody('')
79+
80+
}
81+
82+
const deleteMessage = async (id) => {
83+
await databases.deleteDocument(DATABASE_ID, COLLECTION_ID_MESSAGES, id);
84+
//setMessages(prevState => prevState.filter(message => message.$id !== message_id))
85+
}
86+
87+
return (
88+
<main className="container">
89+
<Header/>
90+
<div className="room--container">
91+
92+
<form id="message--form" onSubmit={handleSubmit}>
93+
<div>
94+
<textarea
95+
required
96+
maxlength="250"
97+
placeholder="Say something..."
98+
onChange={(e) => {setMessageBody(e.target.value)}}
99+
value={messageBody}
100+
></textarea>
101+
</div>
102+
103+
<div className="send-btn--wrapper">
104+
<input className="btn btn--secondary" type="submit" value="send"/>
105+
</div>
106+
</form>
107+
108+
109+
<div>
110+
{messages.map(message => (
111+
<div key={message.$id} className={"message--wrapper"}>
112+
<div className="message--header">
113+
<p>
114+
{message?.username ? (
115+
<span> {message?.username}</span>
116+
): (
117+
'Anonymous user'
118+
)}
119+
120+
<small className="message-timestamp"> {new Date(message.$createdAt).toLocaleString()}</small>
121+
</p>
122+
123+
{message.$permissions.includes(`delete(\"user:${user.$id}\")`) && (
124+
<Trash2 className="delete--btn" onClick={() => {deleteMessage(message.$id)}}/>
125+
126+
)}
127+
</div>
128+
129+
<div className={"message--body" + (message.user_id === user.$id ? ' message--body--owner' : '')}>
130+
<span>{message.body}</span>
131+
132+
</div>
133+
134+
135+
</div>
136+
))}
137+
</div>
138+
</div>
139+
</main>
140+
)
141+
}
142+
143+
export default Room

‎src/utils/AuthContext.jsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { createContext, useState, useEffect, useContext } from "react";
2+
import { account } from "../appwriteConfig";
3+
import { useNavigate } from "react-router";
4+
import { ID} from 'appwrite';
5+
6+
const AuthContext = createContext()
7+
8+
export const AuthProvider = ({children}) => {
9+
const [loading, setLoading] = useState(true)
10+
const [user, setUser] = useState(null)
11+
const navigate = useNavigate()
12+
13+
useEffect(() => {
14+
getUserOnLoad()
15+
}, [])
16+
17+
const getUserOnLoad = async () => {
18+
try{
19+
let accountDetails = await account.get();
20+
setUser(accountDetails)
21+
}catch(error){
22+
23+
}
24+
setLoading(false)
25+
}
26+
27+
const handleUserLogin = async (e, credentials) => {
28+
e.preventDefault()
29+
console.log('CREDS:', credentials)
30+
31+
try{
32+
let response = await account.createEmailSession(credentials.email, credentials.password)
33+
let accountDetails = await account.get();
34+
setUser(accountDetails)
35+
navigate('/')
36+
}catch(error){
37+
console.error(error)
38+
}
39+
}
40+
41+
const handleLogout = async () => {
42+
const response = await account.deleteSession('current');
43+
setUser(null)
44+
}
45+
46+
const handleRegister = async (e, credentials) => {
47+
e.preventDefault()
48+
console.log('Handle Register triggered!', credentials)
49+
50+
if(credentials.password1 !== credentials.password2){
51+
alert('Passwords did not match!')
52+
return
53+
}
54+
55+
try{
56+
57+
let response = await account.create(ID.unique(), credentials.email, credentials.password1, credentials.name);
58+
console.log('User registered!', response)
59+
60+
await account.createEmailSession(credentials.email, credentials.password1)
61+
let accountDetails = await account.get();
62+
setUser(accountDetails)
63+
navigate('/')
64+
}catch(error){
65+
console.error(error)
66+
}
67+
}
68+
69+
const contextData = {
70+
user,
71+
handleUserLogin,
72+
handleLogout,
73+
handleRegister
74+
}
75+
76+
return(
77+
<AuthContext.Provider value={contextData}>
78+
{loading ? <p>Loading...</p> : children}
79+
</AuthContext.Provider>
80+
)
81+
}
82+
83+
export const useAuth = ()=> {return useContext(AuthContext)}
84+
85+
export default AuthContext;

‎src/utils/PrivateRoutes.jsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react'
2+
import { Outlet, Navigate } from 'react-router-dom'
3+
import { useAuth } from './AuthContext'
4+
5+
const PrivateRoutes = () => {
6+
const {user} = useAuth()
7+
return (
8+
<>
9+
{user ? <Outlet/> : <Navigate to="/login"/>}
10+
</>
11+
)
12+
}
13+
14+
export default PrivateRoutes

‎vite.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vite'
2+
import react from '@vitejs/plugin-react'
3+
4+
// https://vitejs.dev/config/
5+
export default defineConfig({
6+
plugins: [react()],
7+
})

0 commit comments

Comments
 (0)
Please sign in to comment.