Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jasper #2

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e6fe784
comfortable completed
jasperteo Dec 7, 2023
32c7425
touched up css
jasperteo Dec 7, 2023
06feb68
siwtched timestamp and message aroud
jasperteo Dec 7, 2023
ffdd2b9
added delete all
jasperteo Dec 7, 2023
e0eb44f
added delete for individual message
jasperteo Dec 8, 2023
3e0c123
added edit
jasperteo Dec 9, 2023
a26fe48
more comfortable done
jasperteo Dec 9, 2023
a24de48
fixed editing bug
jasperteo Dec 10, 2023
c58fb16
Create static.yml
jasperteo Dec 11, 2023
52ae5f2
Merge branch 'main' of github.com:jasperteo/instagram-3.2
jasperteo Dec 11, 2023
1427c80
added uploading of images
jasperteo Dec 12, 2023
89ff66e
added likes
jasperteo Dec 15, 2023
1d4ff8b
changed font, fixed like icon
jasperteo Dec 15, 2023
1e45c44
disabled all other edit buttons while editing, replaced LIKE icon fro…
jasperteo Dec 15, 2023
5a476ab
delete storage object together with message
jasperteo Dec 16, 2023
ead4dbf
fixed bug with likes, added basic sign up and log in
jasperteo Dec 17, 2023
2844b21
added user authentication
jasperteo Dec 17, 2023
2bb009c
accomodated likes for different users
jasperteo Dec 18, 2023
03a7e45
updated dependencies
jasperteo Dec 19, 2023
7201c8e
seperated out Composer
jasperteo Dec 19, 2023
6efae82
made edit and delete and like user dependent
jasperteo Dec 19, 2023
833af93
refactor out NewsFeed
jasperteo Dec 19, 2023
1198896
detects logged in state
jasperteo Dec 21, 2023
12d2d68
added router
jasperteo Dec 21, 2023
1a2cac5
fixed bug with github pages
jasperteo Dec 22, 2023
cbb10b6
github pages SPA url 404 error fix
jasperteo Dec 22, 2023
142c886
undo fix
jasperteo Dec 22, 2023
7c32ef3
trying fix
jasperteo Dec 22, 2023
0e6584a
undo fix
jasperteo Dec 22, 2023
ceeac0a
replaced browser router with hash router
jasperteo Dec 22, 2023
bd20721
useEffect fixes
jasperteo Dec 29, 2023
49a7d60
added detach listener function to useEffect for strict mode
jasperteo Dec 29, 2023
72c9cca
changed app build command
jasperteo Dec 29, 2023
b1e2b5a
firebase hosting
jasperteo Jan 2, 2024
637d79d
firebase hosting
jasperteo Jan 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .firebase/hosting.ZGlzdA.cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
index.html,1704176534558,78dbd630820dbab73673c587c12b69f1fcab3a54b55b856404286d483dd14b19
assets/index-QMTPcTcZ.css,1704176534558,ae20ab9e32f733d718e58156fdfe7e626402487f2b517c774b69b968a80c5fe7
logo.png,1704176534434,a4f3d73a96357dc00ef4132fd3c12aafb62a1493729a9d6f97252a8d8bcd532d
JetBrainsMono-Light.woff2,1704176534432,5117442cda8e5a3cafaafbbdf6758ed37f7ce347798b4e537a2168ad0e3bd88f
SourceSans3VF-Upright.ttf.woff2,1704176534434,a5e8c4da5456f6b9675784affac017e8d160cf3983598e323352cfae09332c5e
assets/index-AxPC6F22.js,1704176534558,4579863da50676eca6a81f5634280deabf43506db41948b0ed6acd8863255747
5 changes: 5 additions & 0 deletions .firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projects": {
"default": "rocketgram-b65c3"
}
}
Binary file added bun.lockb
Binary file not shown.
12 changes: 12 additions & 0 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"hosting": {
"public": "dist",
"ignore": [],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
7 changes: 4 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<title>Rocketgram</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
</body>
</html>
19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@
"preview": "vite preview"
},
"dependencies": {
"firebase": "^10.5.2",
"firebase": "^10.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.56.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"vite": "^4.4.5"
"eslint-plugin-react-refresh": "^0.4.5",
"vite": "^5.0.10"
}
}
Binary file added public/JetBrainsMono-Light.woff2
Binary file not shown.
Binary file added public/SourceSans3VF-Upright.ttf.woff2
Binary file not shown.
45 changes: 45 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,49 @@

.card {
padding: 2em;
width: 50vw;
height: 30vw;
overflow: auto;
}

.message-box {
text-align: left;
display: flex;
flex-direction: column-reverse;
li {
padding: 0.5em 0em 0.5em 0em;
* {
margin: 0.2em;
}
}
}

input {
font-size: 1.25em;
}

.text-messages {
font-size: 1.25em;
font-weight: 420;
width: 15em;
display: inline-flex;
text-wrap: balance;
}

.buttons {
font-size: 0.8125em;
display: inline-flex;
}

.info {
font-family: "JetBrains Mono", monospace;
font-weight: 300;
font-size: 0.5625em;
letter-spacing: 0.075em;
font-feature-settings: "cv18", "zero";
}

iconify-icon {
position: relative;
top: 0.35em;
}
88 changes: 54 additions & 34 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,71 @@
import { useState, useEffect } from "react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer browserRouter implementations over hash routers, we discussed how you can fix this in the morning standup sessions

import { createHashRouter, RouterProvider } from "react-router-dom";
import Composer from "./Composer";
import NewsFeed from "./NewsFeed";
import AuthForm from "./AuthForm";
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "./firebase";
import logo from "/logo.png";
import "./App.css";
import { onChildAdded, push, ref, set } from "firebase/database";
import { database } from "./firebase";
import { useState, useEffect } from "react";

// Save the Firebase message folder name as a constant to avoid bugs due to misspelling
const DB_MESSAGES_KEY = "messages";
import NavBar from "./NavBar";

function App() {
const [messages, setMessages] = useState([]);
export default function App() {
//check login status
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work lifting up the required states for the application to run smoothly

const [isLoggedIn, setIsLoggedIn] = useState(false);
//user info
const [email, setEmail] = useState("");
const [uid, setUid] = useState("");

useEffect(() => {
const messagesRef = ref(database, DB_MESSAGES_KEY);
// onChildAdded will return data for every child at the reference and every subsequent new child
onChildAdded(messagesRef, (data) => {
// Add the subsequent child to local component state, initialising a new array to trigger re-render
setMessages((prevState) =>
// Store message key so we can use it as a key in our list items when rendering messages
[...prevState, { key: data.key, val: data.val() }]
);
onAuthStateChanged(auth, (user) => {
if (user) {
setIsLoggedIn(true);
setEmail(auth.currentUser.email);
setUid(auth.currentUser.uid);
} else setIsLoggedIn(false);
});
}, []);

const writeData = () => {
const messageListRef = ref(database, DB_MESSAGES_KEY);
const newMessageRef = push(messageListRef);
set(newMessageRef, "abc");
};

// Convert messages in state to message JSX elements to render
let messageListItems = messages.map((message) => (
<li key={message.key}>{message.val}</li>
));
const router = createHashRouter([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use nested Routing along with an outlet to reduce the number of times you load and reload the navbar

{
path: "/",
element: (
<>
<NavBar />
<AuthForm
isLoggedIn={isLoggedIn}
email={email}
setEmail={setEmail}
setUid={setUid}
/>
</>
),
},
{
path: "/NewsFeed",
element: (
<>
<NavBar />
{isLoggedIn && <Composer uid={uid} email={email} />}
<div className="card">
<ul
className="message-box"
style={{ borderBottom: "1px dotted white" }}>
<NewsFeed isLoggedIn={isLoggedIn} uid={uid} />
</ul>
</div>
</>
),
},
]);

return (
<>
<div>
<img src={logo} className="logo" alt="Rocket logo" />
</div>
<h1>Instagram Bootcamp</h1>
<div className="card">
{/* TODO: Add input field and add text input as messages in Firebase */}
<button onClick={writeData}>Send</button>
<ol>{messageListItems}</ol>
</div>
<h1>Rocketgram</h1>
<RouterProvider router={router} />
</>
);
}

export default App;
92 changes: 92 additions & 0 deletions src/AuthForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useState } from "react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work keeping all auth logic in one component and either showing the form to login or the button ton sign out, really smart UI and UX thought!

import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { auth } from "./firebase";

export default function AuthForm({ isLoggedIn, email, setEmail, setUid }) {
//Login
const [emailValue, setEmailValue] = useState("");
const [passwordValue, setPasswordValue] = useState("");
//error
const [errorMsg, setErrorMsg] = useState("");

const signUp = async () => {
try {
const userCredential = await createUserWithEmailAndPassword(
auth,
emailValue,
passwordValue
);
console.log(userCredential);
setErrorMsg("");
} catch (error) {
setErrorMsg(error.message);
}
};

const logIn = async () => {
try {
const userCredential = await signInWithEmailAndPassword(
auth,
emailValue,
passwordValue
);
console.log(userCredential);
setErrorMsg("");
} catch (error) {
setErrorMsg(error.message);
}
};

const logOut = async () => {
try {
await signOut(auth);
setErrorMsg("");
setUid("");
setEmail("");
} catch (error) {
setErrorMsg(error.message);
}
};
return (
<>
{!isLoggedIn ? (
<>
<p>
<label htmlFor="email">E-mail: </label>
<input
id="email"
type="email"
autoComplete="on"
pattern=".+@example\.com"
value={emailValue}
onChange={(e) => setEmailValue(e.target.value)}
/>
</p>
<p>
<label htmlFor="password">Password: </label>
<input
id="password"
type="password"
autoComplete="on"
value={passwordValue}
onChange={(e) => setPasswordValue(e.target.value)}
/>
</p>
<p>{errorMsg}</p>
<button onClick={signUp}>Sign Up</button>
<button onClick={logIn}>Login</button>
</>
) : (
<>
<p>{errorMsg}</p>
<p>Welcome: {email}</p>
<button onClick={logOut}>Logout</button>
</>
)}
</>
);
}
69 changes: 69 additions & 0 deletions src/Composer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useState } from "react";
import { push, set, ref as databaseRef } from "firebase/database";
import {
getDownloadURL,
uploadBytes,
ref as storageRef,
} from "firebase/storage";
import { database, storage } from "./firebase";

const DB_MESSAGES_KEY = "messages";
const DB_IMAGES_KEY = "images";

export default function Composer({ uid, email }) {
const [inputValue, setInputValue] = useState("");
const [file, setFile] = useState(null);

const messagesRef = databaseRef(database, DB_MESSAGES_KEY);

const writeData = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice implementation of async and await code!

let name = "";
let url = "";
if (file) {
const newStorageRef = storageRef(
storage,
DB_IMAGES_KEY + "/" + file.name
);
await uploadBytes(newStorageRef, file);
url = await getDownloadURL(newStorageRef);
name = file.name;
}
set(push(messagesRef), {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great implementation of likes to ensure that users can only like once, I also like that you have a like Count such that you dont need to process firebase data every load.

timestamp: `${new Date()}`,
edited: "",
message: inputValue,
fileName: name,
fileUrl: url,
likeCount: 0,
poster: uid,
posterEmail: email,
like: { [uid]: false },
});
setInputValue("");
setFile(null);
};

return (
<form onSubmit={(e) => (e.preventDefault(), e.target.reset())}>
<input
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use some stylesheets to reduce repeated code

name="message-input"
style={{ width: "14.6em", marginRight: "1.4em" }}
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button
style={{ margin: "0.5em" }}
disabled={!inputValue}
onClick={writeData}>
Send
</button>
<br />
<input
style={{ margin: "0.5em" }}
type="file"
onChange={(e) => setFile(e.target.files[0])}
/>
</form>
);
}
Loading