From d4d994fe2684cd9128f27b0c1f62da00d189792d Mon Sep 17 00:00:00 2001 From: Nicholas Pease Date: Sun, 7 Apr 2024 00:38:07 -0400 Subject: [PATCH 1/3] Friending V1 --- frontend-next/src/app/app/page.js | 2 - frontend-next/src/app/user/page.js | 27 ++++++- .../src/components/app/friends/friends.js | 78 +++++++++++++++++++ .../src/components/app/sidebar/home.js | 56 +++++++++++++ 4 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 frontend-next/src/components/app/friends/friends.js diff --git a/frontend-next/src/app/app/page.js b/frontend-next/src/app/app/page.js index 635df25..521d193 100644 --- a/frontend-next/src/app/app/page.js +++ b/frontend-next/src/app/app/page.js @@ -51,8 +51,6 @@ function Home() { window.location.href = "/onboarding"; } }); - } else { - window.location.href = "/login" } }, [authUser]) diff --git a/frontend-next/src/app/user/page.js b/frontend-next/src/app/user/page.js index fc45735..7db4219 100644 --- a/frontend-next/src/app/user/page.js +++ b/frontend-next/src/app/user/page.js @@ -15,6 +15,9 @@ import { Interest } from "../../components/app/profile/Interest"; // Header Import import { Header } from "../../components/app/header"; +// Friend Import +import { addFriend } from "../../components/app/friends/friends"; + /** * User Profile Page * @returns {Object} - User Profile Page @@ -26,6 +29,8 @@ function UserProfile() { const [userInterestArray, setUserInterestArray] = useState(null); // Array of user's interests const [userRoomsArray, setUserRoomsArray] = useState(null); // Array of user's rooms const [isOwner, setIsOwner] = useState(false); // Determines if user is owner of profile + const [friends, setFriends] = useState(false); // is user a friend? + const [isPending, setPending] = useState(false); // is friend request pending? // Handles Edit State in Component, shares useState with ProfileEdit const [isEditing, setIsEditing] = useState(false); @@ -91,6 +96,18 @@ function UserProfile() { }); }, []); + useEffect(() => { + if (user && profileData) { + console.log(user.uid in profileData.friends.requests) + if ("friends" in user) { + profileData.uid in user.friends.friends ? setFriends(true) : setFriends(false); + } + if ("friends" in profileData) { + user.uid in profileData.friends.requests ? setPending(true) : setPending(false); + } + } + }, [user, profileData]); + return (
{isAuthenticated && ( @@ -128,11 +145,13 @@ function UserProfile() { Edit Profile )} - {!isOwner && ( - - Add Friend - + {(!isOwner && !friends) && ( +
+ {(!isPending ) && ( {addFriend(user, profileData.uid);setPending(true)}} className="w-[120px] p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full text-center">Add Friend)} + {(isPending ) && (Pending)} +
)} + {(!isOwner && friends ) && (
Friends
)}
)} diff --git a/frontend-next/src/components/app/friends/friends.js b/frontend-next/src/components/app/friends/friends.js new file mode 100644 index 0000000..addc42b --- /dev/null +++ b/frontend-next/src/components/app/friends/friends.js @@ -0,0 +1,78 @@ +import Link from 'next/link' +import ChatIcon from '@mui/icons-material/Chat'; + +import { database } from "../../../../firebase-config" +import { ref, set, get } from "firebase/database"; +import { useEffect, useState } from 'react'; + +// Icons +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; + +/** + * Send a friend request to a user + * @param {JSON} user - User Object + * @param {JSON} uid - User ID of the user to send a friend request to + */ +export function addFriend(user, uid) { + // Add to user's friend requests + set(ref(database, `users/${uid}/friends/requests/${user.uid}`), { + uid: user.uid + }) +} + +/** + * + * @param {JSON} friendObj - Friend Object (user) + * @returns Friend Component + */ +export function Friend({friendObj}) { + return ( +
+
+
+ +
+
+ +
+
+
{friendObj.firstName} {friendObj.lastName}
+
@{friendObj.username}
+
+ +
+
+
+ ) +} + +export function FriendRequest({user, requestingUser}) { + function acceptRequest(user, uid) { + // Add to user's friends + set(ref(database, `users/${user.uid}/friends/friends/${uid}`), { + uid: uid + }) + // Remove from user's friend requests + set(ref(database, `users/${user.uid}/friends/requests/${uid}`), null) + } + return ( +
+
+
+ {acceptRequest(user, requestingUser.uid)}}/> + +
+
+ +
+
+
{requestingUser.firstName} {requestingUser.lastName}
+
@{requestingUser.username}
+
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/frontend-next/src/components/app/sidebar/home.js b/frontend-next/src/components/app/sidebar/home.js index 74ad684..f868a4a 100644 --- a/frontend-next/src/components/app/sidebar/home.js +++ b/frontend-next/src/components/app/sidebar/home.js @@ -12,6 +12,8 @@ import { ref, set, get } from "firebase/database"; // Component Imports import { ChatRoomSidebar } from "../datatypes"; +// Friend Imports (TEMP) +import { Friend, FriendRequest } from "../friends/friends"; /** * Create Room Component for /app Sidebar @@ -85,6 +87,8 @@ function classNames(...classes) { export function Sidebar({user,location,loadingLoc}) { const [nearbyArr, setNearbyArr] = useState([]) const [nearbyArrReady, setNearbyArrReady] = useState(false) + const [friends, setFriends] = useState([]) + const [friendRequests, setFriendRequests] = useState(null) // Add myRooms to Sidebar var myRoomArr = []; for (var room in user.rooms) { @@ -125,6 +129,27 @@ export function Sidebar({user,location,loadingLoc}) { } }, [location]) + useEffect(() => { + if (user && user.friends) { + get(ref(database, "/users/")).then((snapshot) => { + var users = snapshot.val(); + var friends = []; + for (var friend in user.friends.friends) { + friends.push(); + } + setFriends(friends); + }); + + var requestArr = []; + for (var request in user.friends.requests) { + get(ref(database, `/users/${request}`)).then((snapshot) => { + requestArr.push(); + }); + } + setFriendRequests(requestArr); + } + }, [user]) + return (
@@ -151,6 +176,28 @@ export function Sidebar({user,location,loadingLoc}) { ? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black' : 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold' )}>Create + + classNames( + 'w-[30%]', + selected + ? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black' + : 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold' + )} defaultIndex={1}>DMs + + classNames( + 'w-[30%]', + selected + ? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black' + : 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold' + )} defaultIndex={1}>Friends + + classNames( + 'w-[30%]', + selected + ? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black' + : 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold' + )} defaultIndex={1}>Requests + @@ -173,6 +220,15 @@ export function Sidebar({user,location,loadingLoc}) { {!loadingLoc && } {loadingLoc &&
Loading...
}
+ + DMs + + + {friends} + + + {friendRequests} +
-- 2.52.0 From 1dda82790c4aabfa120036d5902186808ec3809e Mon Sep 17 00:00:00 2001 From: Nicholas Pease Date: Sun, 7 Apr 2024 03:05:16 -0400 Subject: [PATCH 2/3] DM's Working --- frontend-next/src/app/dm/layout.js | 17 +++ frontend-next/src/app/dm/page.js | 139 ++++++++++++++++++ .../src/components/app/friends/dm.js | 55 +++++++ .../src/components/app/friends/friends.js | 33 ++++- .../src/components/app/friends/page.js | 87 +++++++++++ frontend-next/src/components/app/header.js | 2 +- .../src/components/app/sidebar/dm.js | 60 ++++++++ .../src/components/app/sidebar/home.js | 31 +++- 8 files changed, 416 insertions(+), 8 deletions(-) create mode 100644 frontend-next/src/app/dm/layout.js create mode 100644 frontend-next/src/app/dm/page.js create mode 100644 frontend-next/src/components/app/friends/dm.js create mode 100644 frontend-next/src/components/app/friends/page.js create mode 100644 frontend-next/src/components/app/sidebar/dm.js diff --git a/frontend-next/src/app/dm/layout.js b/frontend-next/src/app/dm/layout.js new file mode 100644 index 0000000..fc1a7b4 --- /dev/null +++ b/frontend-next/src/app/dm/layout.js @@ -0,0 +1,17 @@ +import { Inter } from "next/font/google"; +import "../globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata = { + title: "ChatMaps: DM", + description: "ChatMaps: Social Media for College Students", +}; + +export default function RootLayout({ children }) { + return ( + + {children} + + ); +} diff --git a/frontend-next/src/app/dm/page.js b/frontend-next/src/app/dm/page.js new file mode 100644 index 0000000..d2487a5 --- /dev/null +++ b/frontend-next/src/app/dm/page.js @@ -0,0 +1,139 @@ +"use client"; +// System Imports +import Drawer from '@mui/material/Drawer'; +import { useState, useEffect } from "react"; + +// Firebase Imports +import { auth, database } from "../../../firebase-config"; +import { ref, onValue, set, onDisconnect } from "firebase/database"; +import { useAuthState } from "react-firebase-hooks/auth" + +// Component Imports +import { Header } from "../../components/app/header"; +import { DMRoom } from "../../components/app/friends/page"; +import { Sidebar } from "../../components/app/sidebar/dm"; +import {useWindowSize} from "../../components/app/datatypes"; + + +/** + * DM Page + * @returns {Object} Chat Page + */ +function Chat() { + // State variables for chat page + const [user, setUser] = useState(null); // user data + const [chatRoomObj, setChatRoomObj] = useState(null); // Current chatroom object + const [doneLoading, setDoneLoading] = useState(false) // is the page done loading or not + const [authUser] = useAuthState(auth) // auth user object (used to obtain other user object) + const [drawerOpen, setDrawerOpen] = useState(true); // drawer open state + + var windowSize = useWindowSize() + useEffect(() => { + if (windowSize.width < 767) { + setDrawerOpen(false) + } else { + setDrawerOpen(true) + } + }, [windowSize]) + + // Authentication Verification / Redirection if Profile Data not Filled out + useEffect(() => { + if (authUser) { + onValue(ref(database, `users/${authUser.uid}`), (userData) => { + userData = userData.val(); + if (userData) { + setUser(userData); + } else { + window.location.href = "/onboarding"; + } + }); + } + }, [authUser]) + + // Users URL params to load proper chatroom, then logs the user into that room + useEffect(() => { + if (user) { + const searchParams = new URLSearchParams(document.location.search); + var path = searchParams.get("dm") + + /*// Send entered message + var payload = { + body: "entered", + user: user.username, + isSystem: true, + timestamp: new Date().getTime(), + uid: user.uid, + }; + set( + ref( + database, + `/rooms/${path}/chats/${new Date().getTime()}-${user.username}` + ), + payload + );*/ + + // Add user to online for room + set(ref(database, `/dms/${path}/users/online/${user.uid}`), user) + + // Removes user from room on disconnect (reload, window close, internet lost) + onDisconnect(ref(database, `/dms/${path}/users/online/${user.uid}`)).remove() + + // Sends leaving message on disconnect (Timestamp function used due to new onDisconnect stuff) + /*someRef = ref(database, `/rooms/${path}/chats/${new Date().getTime()}-${user.username}`) + onDisconnect(someRef).set({ + body: "left", + user: user.username, + isSystem: true, + timestamp: serverTimestamp(), + uid: user.uid, + })*/ + + onValue(ref(database, `/dms/${path}`), (roomData) => { + roomData = roomData.val(); + setChatRoomObj(roomData) + if (!doneLoading) { + setDoneLoading(true) + } + }) + } + }, [user]); + + return ( +
+ {(authUser && doneLoading) && ( +
+ {/* Left Side of Page */} +
+ {/* Header */} +
{setDrawerOpen(!drawerOpen)}} + /> + {/* Main Page Section */} +
+ +
+
+ {/* Sidebar (Right Side of Page) */} + 767? "persistent": "temporary"} onClose={() => {setDrawerOpen(false)}} sx={{ + width: windowSize.width > 767? 400: "80%", + marginTop: 10, + flexShrink: 0, + '& .MuiDrawer-paper': { + width: windowSize.width > 767? 400: "80%", + borderLeft: 0, + }, + }}> +
+ +
+
+
+ )} +
+ ); +} + +export default Chat; \ No newline at end of file diff --git a/frontend-next/src/components/app/friends/dm.js b/frontend-next/src/components/app/friends/dm.js new file mode 100644 index 0000000..36c07e3 --- /dev/null +++ b/frontend-next/src/components/app/friends/dm.js @@ -0,0 +1,55 @@ +// Firebase Imports +import { database } from "../../../../firebase-config" +import { ref, set, get } from "firebase/database"; + +import ChatIcon from '@mui/icons-material/Chat'; + + +export function openDM(user, uid) { + get(ref(database, `dms/`)).then((snapshot) => { + var dmsList = snapshot.val(); + for (var dmRoom in dmsList) { + if (user.uid in dmsList[dmRoom].UIDs && uid in dmsList[dmRoom].UIDs) { + window.location.href = `/dm?dm=${dmRoom}` + } + } + createDM(user, uid) + window.location.href = `/dm?dm=${user.uid}-${uid}` + }); +} + + +export function createDM(user, uid) { + set(ref(database, `dms/${user.uid}-${uid}`), { + initUID: user.uid, + targetUID: uid, + room: user.uid + "-" + uid, + UIDs: [user.uid, uid] + }) +} + +/** + * + * @param {JSON} friendObj - Friend Object (user) + * @returns DM Component + */ +export function DM({user,friendObj}) { + return ( +
+
+
+ {openDM(user,friendObj.uid)}}/> +
+
+
{openDM(user,friendObj.uid)}}> +
+
+
{friendObj.firstName} {friendObj.lastName}
+
@{friendObj.username}
+
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/frontend-next/src/components/app/friends/friends.js b/frontend-next/src/components/app/friends/friends.js index addc42b..db01bd9 100644 --- a/frontend-next/src/components/app/friends/friends.js +++ b/frontend-next/src/components/app/friends/friends.js @@ -1,9 +1,13 @@ import Link from 'next/link' + +// Icons import ChatIcon from '@mui/icons-material/Chat'; +// Firebase Imports import { database } from "../../../../firebase-config" -import { ref, set, get } from "firebase/database"; -import { useEffect, useState } from 'react'; +import { ref, set } from "firebase/database"; + +import { openDM } from './dm'; // Icons import CheckIcon from '@mui/icons-material/Check'; @@ -26,12 +30,12 @@ export function addFriend(user, uid) { * @param {JSON} friendObj - Friend Object (user) * @returns Friend Component */ -export function Friend({friendObj}) { +export function Friend({user,friendObj}) { return (
- + {openDM(user,friendObj.uid)}}/>
@@ -47,12 +51,31 @@ export function Friend({friendObj}) { ) } +/** + * + * @prop {JSON} user - User Object + * @prop {JSON} requestingUser - User Object of the user requesting to be friends + * @returns + */ export function FriendRequest({user, requestingUser}) { + /** + * Accepts Friend Request + * @param {JSON} user - User Object + * @param {JSON} uid - User ID of the user who sent the friend request + */ function acceptRequest(user, uid) { // Add to user's friends set(ref(database, `users/${user.uid}/friends/friends/${uid}`), { uid: uid }) + removeRequest(user, uid) + } + /** + * Removes Friend Request + * @param {JSON} user - User Object + * @param {JSON} uid - User ID of the user who sent the friend request + */ + function removeRequest(user, uid) { // Remove from user's friend requests set(ref(database, `users/${user.uid}/friends/requests/${uid}`), null) } @@ -61,7 +84,7 @@ export function FriendRequest({user, requestingUser}) {
{acceptRequest(user, requestingUser.uid)}}/> - + {removeRequest(user, requestingUser.uid)}}/>
diff --git a/frontend-next/src/components/app/friends/page.js b/frontend-next/src/components/app/friends/page.js new file mode 100644 index 0000000..c9698a8 --- /dev/null +++ b/frontend-next/src/components/app/friends/page.js @@ -0,0 +1,87 @@ +// Dependency Imports +import { Form, useForm } from "react-hook-form"; + +// Firebase Imports +import { ref, set } from "firebase/database"; +import { database } from "../../../../firebase-config"; + +// Component Imports +import { Chat, SystemMessage } from "../datatypes"; + +// Icons +import SendIcon from '@mui/icons-material/Send'; + +/** + * Chat Room Component + * @prop {JSON} roomObj - Room Object + * @prop {JSON} user - User Object + * @returns {Object} - Chat Room Component + */ +export function DMRoom({ roomObj, user }) { + var { register, control, reset, handleSubmit } = useForm(); + + // Message updater + var chatsArr = []; + var messages = roomObj.chats; + for (var message in messages) { + if (messages[message].isSystem) { + chatsArr.push( + + ); + } else { + chatsArr.push( + + ); + } + } + var chats = chatsArr.reverse(); + + /** + * Send Message in Chatroom + * @param {JSON} data - Message data to send (from form) + * @returns {void} + */ + function sendMessage(data) { + reset(); + var payload = { + body: data.message, + user: user.username, + uid: user.uid, + isSystem: false, + timestamp: new Date().getTime(), + }; + set( + ref( + database, + `/dms/${roomObj.room}/chats/${new Date().getTime()}-${user.username}` + ), + payload + ); + } + + if (!chats) return
No Chats
; + return ( +
+
+ {chats} +
+
+
+
+ + +
+
+
+
+ ); +} diff --git a/frontend-next/src/components/app/header.js b/frontend-next/src/components/app/header.js index 3120d64..aa860c8 100644 --- a/frontend-next/src/components/app/header.js +++ b/frontend-next/src/components/app/header.js @@ -119,7 +119,7 @@ export function Header({mainTab,chatRoomObj,user,sidebarControl}) { )} - {mainTab == "chat" && ( + {(mainTab == "chat" || mainTab == "dm" ) && ( ); + var chatroomOnline = activeUsers + } + + useEffect(() => { + if (user) { + // Profile Information + if (user.uid == chatRoomObj.UIDs[0]) { + var profileUID = chatRoomObj.UIDs[1] + } else { + var profileUID = chatRoomObj.UIDs[0] + } + + get(ref(database, `users/${profileUID}`)).then((snapshot) => { + var profileData = snapshot.val() + setProfileData(profileData) + }) + } + }, [user]) + + return ( +
+ {profileData && ( +
+
+
+
+ +
{profileData.firstName} {profileData.lastName}
+ @{profileData.username} +
+
+
+
In The Chat
+ {chatroomOnline} +
+
+
+ )} +
+ ); + } + \ No newline at end of file diff --git a/frontend-next/src/components/app/sidebar/home.js b/frontend-next/src/components/app/sidebar/home.js index f868a4a..f7744c8 100644 --- a/frontend-next/src/components/app/sidebar/home.js +++ b/frontend-next/src/components/app/sidebar/home.js @@ -15,6 +15,9 @@ import { ChatRoomSidebar } from "../datatypes"; // Friend Imports (TEMP) import { Friend, FriendRequest } from "../friends/friends"; +// DM Imports +import { DM } from "../friends/dm"; + /** * Create Room Component for /app Sidebar * @prop {JSON} loc - Location Object (latitude, longitude) @@ -89,6 +92,7 @@ export function Sidebar({user,location,loadingLoc}) { const [nearbyArrReady, setNearbyArrReady] = useState(false) const [friends, setFriends] = useState([]) const [friendRequests, setFriendRequests] = useState(null) + const [dms, setDMs] = useState(null) // Add myRooms to Sidebar var myRoomArr = []; for (var room in user.rooms) { @@ -135,7 +139,7 @@ export function Sidebar({user,location,loadingLoc}) { var users = snapshot.val(); var friends = []; for (var friend in user.friends.friends) { - friends.push(); + friends.push(); } setFriends(friends); }); @@ -147,7 +151,30 @@ export function Sidebar({user,location,loadingLoc}) { }); } setFriendRequests(requestArr); + } else { + setFriends(
No Friends
); + setFriendRequests(
No Friend Requests
); } + + get(ref(database, `/dms`)).then((snapshot) => { + var dmsList = snapshot.val(); + var dmArr = []; + for(var dmRoom in dmsList) { + if (user.uid == dmsList[dmRoom].UIDs[0]) { + get(ref(database, `/users/${dmsList[dmRoom].UIDs[1]}`)).then((snapshot) => { + dmArr.push(); + }) + } else if (user.uid == dmsList[dmRoom].UIDs[1]) { + get(ref(database, `/users/${dmsList[dmRoom].UIDs[1]}`)).then((snapshot) => { + dmArr.push(); + }) + } + } + if (dmArr.length == 0) { + dmArr.push(
No DMs
); + } + setDMs(dmArr); + }) }, [user]) return ( @@ -221,7 +248,7 @@ export function Sidebar({user,location,loadingLoc}) { {loadingLoc &&
Loading...
} - DMs + {dms} {friends} -- 2.52.0 From e8be631182d18087ef696f2b36ee559988607721 Mon Sep 17 00:00:00 2001 From: Nicholas Pease Date: Sun, 7 Apr 2024 03:07:22 -0400 Subject: [PATCH 3/3] Fixes for Login Bug --- frontend-next/src/app/chat/page.js | 2 -- frontend-next/src/app/user/page.js | 3 --- 2 files changed, 5 deletions(-) diff --git a/frontend-next/src/app/chat/page.js b/frontend-next/src/app/chat/page.js index c9c13f4..9421a3c 100644 --- a/frontend-next/src/app/chat/page.js +++ b/frontend-next/src/app/chat/page.js @@ -47,8 +47,6 @@ function Chat() { window.location.href = "/onboarding"; } }); - } else { - window.location.href = "/login" } }, [authUser]) diff --git a/frontend-next/src/app/user/page.js b/frontend-next/src/app/user/page.js index 7db4219..f56df4f 100644 --- a/frontend-next/src/app/user/page.js +++ b/frontend-next/src/app/user/page.js @@ -56,9 +56,6 @@ function UserProfile() { window.location.href = "/onboarding"; } }); - } else { - setIsAuthenticated(false); - window.location.href = "/login"; } }); }, []); -- 2.52.0