diff --git a/frontend-next/src/app/api/firebase-config.js b/frontend-next/firebase-config.js similarity index 84% rename from frontend-next/src/app/api/firebase-config.js rename to frontend-next/firebase-config.js index c144b01..cb89f51 100644 --- a/frontend-next/src/app/api/firebase-config.js +++ b/frontend-next/firebase-config.js @@ -1,6 +1,7 @@ import { initializeApp, getApps, getApp } from "firebase/app"; import { getAuth } from "firebase/auth"; import { getDatabase} from "firebase/database" +import {getStorage} from "firebase/storage" var config = { apiKey: "AIzaSyDbDPjQGt-lIjNPeTG-Q5AECM1m0vtOr2c", @@ -14,6 +15,7 @@ var config = { var app = getApps().length > 0 ? getApp() : initializeApp(config); var auth = getAuth(app); +var storage = getStorage(app); var database = getDatabase(app); -export { auth, app, database }; \ No newline at end of file +export { auth, app, database, storage }; \ No newline at end of file diff --git a/frontend-next/package-lock.json b/frontend-next/package-lock.json index be1e38b..e515ae0 100644 --- a/frontend-next/package-lock.json +++ b/frontend-next/package-lock.json @@ -8,6 +8,7 @@ "name": "chatmaps", "version": "0.1.0", "dependencies": { + "@headlessui/react": "^1.7.18", "firebase": "^10.6.0", "next": "^14.1.0", "pigeon-maps": "^0.21.3", @@ -627,6 +628,22 @@ "node": ">=6" } }, + "node_modules/@headlessui/react": { + "version": "1.7.18", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.18.tgz", + "integrity": "sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==", + "dependencies": { + "@tanstack/react-virtual": "^3.0.0-beta.60", + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1014,6 +1031,31 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.2.0.tgz", + "integrity": "sha512-OEdMByf2hEfDa6XDbGlZN8qO6bTjlNKqjM3im9JG+u3mCL8jALy0T/67oDI001raUUPh1Bdmfn4ZvPOV5knpcg==", + "dependencies": { + "@tanstack/virtual-core": "3.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.2.0.tgz", + "integrity": "sha512-P5XgYoAw/vfW65byBbJQCw+cagdXDT/qH6wmABiLt4v4YBT2q2vqCOhihe+D1Nt325F/S/0Tkv6C5z0Lv+VBQQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", diff --git a/frontend-next/package.json b/frontend-next/package.json index 48282de..17528e8 100644 --- a/frontend-next/package.json +++ b/frontend-next/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "@headlessui/react": "^1.7.18", "firebase": "^10.6.0", "next": "^14.1.0", "pigeon-maps": "^0.21.3", diff --git a/frontend-next/src/app/app/page.js b/frontend-next/src/app/app/page.js index 9a6a1da..b0e0a5b 100644 --- a/frontend-next/src/app/app/page.js +++ b/frontend-next/src/app/app/page.js @@ -1,12 +1,12 @@ "use client"; // System Imports import { useState, useEffect } from "react"; -import { auth, database } from "../api/firebase-config"; +import { auth, database } from "../../../firebase-config"; import { ref, onValue, set, remove, get } from "firebase/database"; import { useBeforeunload } from "react-beforeunload"; -import {useRouter} from "next/navigation"; import {Marker} from "pigeon-maps"; -import {onAuthStateChanged, signOut} from "firebase/auth" +import {onAuthStateChanged} from "firebase/auth" + // Refactored Component Imports // Data Structure Imports @@ -45,6 +45,19 @@ function Home() { const [markers, setMarkers] = useState([]); const [isAuthenticated, setAuth] = useState(false) const [user, setUser] = useState(null) + const [usingSearchParams, setUsingSearchParams] = useState(true) + + useEffect(() => { + const searchParams = new URLSearchParams(document.location.search); + var roomSwitch = null + if (searchParams.has("room") && usingSearchParams && user) { + roomSwitch = searchParams.get("room") + setUsingSearchParams(false) + get(ref(database, `rooms/${searchParams.get("room")}`)).then((snapshot) => { + selectChatRoom(snapshot.val()) + }); + } + }, [user]) // Authentication useEffect(() => { @@ -53,7 +66,6 @@ function Home() { get(ref(database, `users/${user.uid}`)) .then((userData) => { userData = userData.val() - console.log(userData) if (userData) { setUser(userData) setAuth(true) @@ -69,7 +81,6 @@ function Home() { }) }, []) - // Grabs user data, saves to user, then lists the users saved rooms useEffect(() => { if (user) { @@ -141,7 +152,6 @@ function Home() { } },[user]); - // Dont Double Send Leaving Message useEffect(() => { if (myRoomsObj && chatRoomObj) { @@ -169,6 +179,7 @@ function Home() { user: user.username, isSystem: true, timestamp: new Date().getTime(), + uid: user.uid }; set( ref( @@ -197,10 +208,6 @@ function Home() { } // Users who added to "my rooms" - console.log( - snapshot.val().hasOwnProperty("users") && - snapshot.val().users.hasOwnProperty("all") - ); if ( snapshot.val().hasOwnProperty("users") && snapshot.val().users.hasOwnProperty("all") @@ -225,6 +232,7 @@ function Home() { user: user.username, isSystem: true, timestamp: new Date().getTime(), + uid: user.uid }; set( ref( @@ -278,7 +286,7 @@ function Home() { )} {mainTab == "chat" && ( - + )} {mainTab == "profile" && ( diff --git a/frontend-next/src/app/login/page.js b/frontend-next/src/app/login/page.js index a544c89..b0ee5aa 100644 --- a/frontend-next/src/app/login/page.js +++ b/frontend-next/src/app/login/page.js @@ -4,8 +4,8 @@ import { useRouter } from "next/navigation"; import "../globals.css" // Firebase imports -import {auth} from "../api/firebase-config"; -import { setPersistence, signInWithEmailAndPassword, browserSessionPersistence } from "firebase/auth"; +import {auth} from "../../../firebase-config"; +import { setPersistence, signInWithEmailAndPassword, indexedDBLocalPersistence } from "firebase/auth"; function Login() { var router = useRouter(); @@ -13,7 +13,7 @@ function Login() { var { register, control, setError, handleSubmit, formState: { errors, isSubmitting, isSubmitted } } = useForm() function authenticate(data) { - setPersistence(auth, browserSessionPersistence) + setPersistence(auth, indexedDBLocalPersistence) .then(() => { signInWithEmailAndPassword(auth,data.email,data.password) .then((userCredential) => { @@ -45,7 +45,7 @@ function Login() { >

-
+
Have an account? Log In diff --git a/frontend-next/src/app/user/[stub]/layout.js b/frontend-next/src/app/user/[stub]/layout.js new file mode 100644 index 0000000..94d85e5 --- /dev/null +++ b/frontend-next/src/app/user/[stub]/layout.js @@ -0,0 +1,19 @@ +import { Inter } from "next/font/google"; +import "../../globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata = { + title: "ChatMaps: User Profile", + description: "ChatMaps: Social Media for College Students", +}; + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ); +} diff --git a/frontend-next/src/app/user/[stub]/page.js b/frontend-next/src/app/user/[stub]/page.js new file mode 100644 index 0000000..d161d90 --- /dev/null +++ b/frontend-next/src/app/user/[stub]/page.js @@ -0,0 +1,191 @@ +"use client"; +// System Imports +import { useState, useEffect } from "react"; +import { auth, database, storage } from "../../../../firebase-config"; +import { ref, onValue, get, update } from "firebase/database"; +import { ref as sRef, getDownloadURL } from "firebase/storage"; +import {onAuthStateChanged} from "firebase/auth" +import { useForm, Form } from "react-hook-form"; + +// Refactored Component Imports +// Data Structure Imports + import { Interest, ProfileRoom } from "../../../components/app/datatypes"; + +// Header Import +import { Header } from "../../../components/app/header"; +import { uploadBytes } from "firebase/storage"; + +// Contains most everything for the app homepage +function Home({ params }) { + // It's time to document and change these awful variable names + // State variables for app page + const [profileData, setProfileData] = useState(null) + const [isAuthenticated, setAuth] = useState(false) + const [user, setUser] = useState(null) + const [userInterestArray, setUserInterestArray] = useState(null) + const [userRoomsArray, setUserRoomsArray] = useState(null) + const [isOwner, setOwn] = useState(false) + const [isEditing, setEdit] = useState(false) + + var { register, control} = useForm() + + // Authentication + useEffect(() => { + onAuthStateChanged(auth, (user) => { + if (user) { + get(ref(database, `users/${user.uid}`)) + .then((userData) => { + userData = userData.val() + if (userData) { + if (userData.uid == params.stub) { + setOwn(true) + } + setUser(userData) + setAuth(true) + } else { + window.location.href = "/onboarding" + } + + }) + } else { + setAuth(false) + window.location.href = "/login" + } + }) + }, []) + + // Grabs profile user data + useEffect(() => { + onValue(ref(database, "/users/" + params.stub), (snapshot) => { + setProfileData(snapshot.val()); + var interests = snapshot.val().interests || null; + interests = interests.split(","); + var interestArray = [] + var i = 0 + for (var interest in interests) { + if (i < 4) + interestArray.push() + i++; + } + setUserInterestArray(interestArray) + var rooms = snapshot.val().rooms; + var roomArray = [] + for (var room in rooms) { + roomArray.push() + } + setUserRoomsArray(roomArray); + }); + },[]); + + function save({data}) { + if (data.pfp[0]) { + // image stuff + uploadBytes(sRef(storage, `users/${user.uid}/pfp`), data.pfp[0]).then(() => { + getDownloadURL(sRef(storage, `users/${user.uid}/pfp`)).then((url) => { + data.pfp = url + for (var key in data) { + if (data[key] == "") { + data[key] = profileData[key] + } + } + + setEdit(false) + update(ref(database, `users/${user.uid}`), data) + }) + }) + } else { + for (var key in data) { + if (data[key] == "") { + data[key] = profileData[key] + } + } + data.pfp = profileData.pfp + setEdit(false) + update(ref(database, `users/${user.uid}`), data) + } + } + + return ( +
+ {isAuthenticated && ( +
+ {/* Left Side of Page */} +
+ {/* Header */} +
+ {/* Main Page Section */} +
+ +
+ {!isEditing && ( +
+ +
+ {profileData.firstName} {profileData.lastName} +
+
@{profileData.username}
+
{profileData.bio}
+
+ {userInterestArray} +
+ +
+ )} + {isEditing && ( +
+
+
+
+ + Current Profile Picture +
+
+ +
+
+
+
+
First Name
+ +
+
+
Last Name
+ +
+
+
Username
+ +
+
+
Interests (Comma Seperated)
+ +
+
+
Bio
+