Update Dev Branch #52
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||

|
||||
Main repo for ChatMaps, our COS420 Project.
|
||||
|
||||
ChatMaps is a web-based social networking service that allows users to connect to others in their local geographic area. It will implement an interactable mapping utility to show general user locations relative to other users, as well as a chat room feature that allows users to start public conversations based on a specified topic. ChatMaps is primarily intended for use in densely populated areas, such as college campuses or metropolitan areas, so people of similar interests can start conversations. The goal of this project is to create a web app that plots locations, gives a radius of the local area, and connects users into different topic-based chat rooms.
|
||||
ChatMaps is a web-based social networking service that allows users to connect to others in their local geographic area. It implements an interactable mapping utility to show general user locations relative to other users, as well as a chat room feature that allows users to start public conversations based on a specified topic. ChatMaps is primarily intended for use in densely populated areas, such as college campuses or metropolitan areas, so people of similar interests can start conversations. The goal of this project is to create a web app that plots locations, gives a radius of the local area, and connects users into different topic-based chat rooms.
|
||||
|
||||
This service will implement user login and profiles, allowing users to add each other as friends and start private conversations. There will be several default chat rooms of varying topics, but users will also have the ability to create their own topics that will be viewable by other users. For example, a user at the University of Maine could create a joinable chat room titled “COS420”, which would be visible to others near this campus.
|
||||
This service implements user login and profiles, allowing users to add each other as friends and start private conversations. There are several default chat rooms of varying topics, but users also have the ability to create their own topics that will be viewable by other users. For example, a user at the University of Maine could create a joinable chat room titled “COS420”, which would be visible to others near this campus.
|
||||
|
||||
This app shares some similarities to other social networks that implement location-based content. ChatMaps’ novel approach is to utilize user location to facilitate real-time communication with others within a given radius.
|
||||
|
||||
@@ -15,6 +15,8 @@ A local version can be run with:
|
||||
|
||||
cd frontend-next/
|
||||
|
||||
npm install
|
||||
|
||||
npm run build
|
||||
|
||||
npm run dev
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,9 +11,7 @@ export const metadata = {
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
+201
-160
@@ -4,9 +4,8 @@ import { useState, useEffect } from "react";
|
||||
import { auth, database } from "../../../firebase-config";
|
||||
import { ref, onValue, set, remove, get } from "firebase/database";
|
||||
import { useBeforeunload } from "react-beforeunload";
|
||||
import {Marker} from "pigeon-maps";
|
||||
import {onAuthStateChanged} from "firebase/auth"
|
||||
|
||||
import { Marker } from "pigeon-maps";
|
||||
import { onAuthStateChanged } from "firebase/auth";
|
||||
|
||||
// Refactored Component Imports
|
||||
// Data Structure Imports
|
||||
@@ -20,7 +19,7 @@ import { MainTabChatRoom } from "../../components/app/main_tab/chat";
|
||||
import { MainTabHome } from "../../components/app/main_tab/home";
|
||||
|
||||
// Sidebar Imports
|
||||
import {Home_Sidebar} from "../../components/app/sidebar/home";
|
||||
import { Home_Sidebar } from "../../components/app/sidebar/home";
|
||||
import { Chat_Sidebar } from "../../components/app/sidebar/chat";
|
||||
import { Profile_Sidebar } from "../../components/app/sidebar/profile";
|
||||
|
||||
@@ -32,100 +31,111 @@ function Home() {
|
||||
const [tab, setTab] = useState("nearby"); // Sidebar Tab
|
||||
const [chatRoomObj, setChatRoomObj] = useState(null); // Current chatroom object
|
||||
const [myRoomsObj, setMyRoomsObj] = useState(null); // My Rooms Object
|
||||
const [myRooms, setRoomData] = useState(null); // Current user saved rooms list
|
||||
const [isRoomLoading, setRoomLoading] = useState(true); // myRooms loading variable, true = myRooms loading, false = finished loading
|
||||
const [roomData, setRoomData] = useState(null); // Current user saved rooms list
|
||||
const [isRoomLoading, setIsRoomLoading] = useState(true); // myRooms loading variable, true = myRooms loading, false = finished loading
|
||||
const [isMyRoom, setIsMyRoom] = useState(false); // Is current room in myRooms? true, false
|
||||
const [location, setLocation] = useState(null); // location variable [lat,long]
|
||||
const [loadingLoc, setLoadingLoc] = useState(true); // location variable loading, true = loading, false = finished loading
|
||||
const [nearby, setNearby] = useState(null); // nearby rooms array
|
||||
const [loadingNearby, setLoadingNearby] = useState(true); // loading nearby rooms array, true = loading, false = finished loading
|
||||
const [chatroomOnline, setChatRoomOnline] = useState(null); // holds online users
|
||||
const [chatroomOnline, setChatroomOnline] = useState(null); // holds online users
|
||||
const [chatroomUsers, setChatroomUsers] = useState(null); // holds all chatroom users
|
||||
const [chatroomUsersLoading, setChatroomUsersLoading] = useState(true);
|
||||
const [markers, setMarkers] = useState([]);
|
||||
const [isAuthenticated, setAuth] = useState(false)
|
||||
const [user, setUser] = useState(null)
|
||||
const [usingSearchParams, setUsingSearchParams] = useState(true)
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [user, setUser] = useState(null);
|
||||
const [usingSearchParams, setUsingSearchParams] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(document.location.search);
|
||||
var roomSwitch = null
|
||||
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())
|
||||
});
|
||||
roomSwitch = searchParams.get("room");
|
||||
setUsingSearchParams(false);
|
||||
get(ref(database, `rooms/${searchParams.get("room")}`)).then(
|
||||
(snapshot) => {
|
||||
selectChatRoom(snapshot.val());
|
||||
}
|
||||
);
|
||||
}
|
||||
}, [user])
|
||||
}, [user]);
|
||||
|
||||
// Authentication
|
||||
useEffect(() => {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user) {
|
||||
get(ref(database, `users/${user.uid}`))
|
||||
.then((userData) => {
|
||||
userData = userData.val()
|
||||
get(ref(database, `users/${user.uid}`)).then((userData) => {
|
||||
userData = userData.val();
|
||||
if (userData) {
|
||||
setUser(userData)
|
||||
setAuth(true)
|
||||
setUser(userData);
|
||||
setIsAuthenticated(true);
|
||||
} else {
|
||||
window.location.href = "/onboarding"
|
||||
window.location.href = "/onboarding";
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
} else {
|
||||
setAuth(false)
|
||||
window.location.href = "/login"
|
||||
setIsAuthenticated(false);
|
||||
window.location.href = "/login";
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Grabs user data, saves to user, then lists the users saved rooms
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
onValue(ref(database, "/users/" + user.uid + "/rooms"), (snapshot) => {
|
||||
setRoomLoading(true);
|
||||
var rooms = snapshot.val();
|
||||
setMyRoomsObj(rooms);
|
||||
var roomArr = [];
|
||||
var markerArr = markers;
|
||||
for (var room in rooms) {
|
||||
var newRoom = (
|
||||
<ChatRoomSidebar
|
||||
roomObj={rooms[room]}
|
||||
key={rooms[room].timestamp}
|
||||
click={selectChatRoom}
|
||||
/>
|
||||
);
|
||||
markerArr.push(
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[rooms[room].latitude, rooms[room].longitude]}
|
||||
color="blue"
|
||||
/>
|
||||
);
|
||||
roomArr.push(newRoom);
|
||||
}
|
||||
setMarkers(markerArr);
|
||||
setRoomData(roomArr);
|
||||
setRoomLoading(false);
|
||||
});
|
||||
if (user) {
|
||||
onValue(ref(database, "/users/" + user.uid + "/rooms"), (snapshot) => {
|
||||
setIsRoomLoading(true);
|
||||
var rooms = snapshot.val();
|
||||
setMyRoomsObj(rooms);
|
||||
var roomArr = [];
|
||||
var markerArr = markers;
|
||||
for (var room in rooms) {
|
||||
var newRoom = (
|
||||
<ChatRoomSidebar
|
||||
roomObj={rooms[room]}
|
||||
key={rooms[room].timestamp}
|
||||
click={selectChatRoom}
|
||||
/>
|
||||
);
|
||||
markerArr.push(
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[rooms[room].latitude, rooms[room].longitude]}
|
||||
color="blue"
|
||||
onClick={() =>
|
||||
(window.location.href =
|
||||
"/app?room=" +
|
||||
rooms[room].path +
|
||||
"/" +
|
||||
rooms[room].name +
|
||||
"-" +
|
||||
rooms[room].timestamp)
|
||||
}
|
||||
/>
|
||||
);
|
||||
roomArr.push(newRoom);
|
||||
}
|
||||
},[user]);
|
||||
setMarkers(markerArr);
|
||||
setRoomData(roomArr);
|
||||
setIsRoomLoading(false);
|
||||
});
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// Grabs the user location
|
||||
useEffect(() => {
|
||||
if ("geolocation" in navigator && user) {
|
||||
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
|
||||
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
||||
|
||||
setLocation(coords)
|
||||
setLoadingLoc(false)
|
||||
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
|
||||
var markersArr = markers
|
||||
setLocation(coords);
|
||||
setLoadingLoc(false);
|
||||
var path =
|
||||
String(coords.latitude.toFixed(2)).replace(".", "") +
|
||||
"/" +
|
||||
String(coords.longitude.toFixed(2)).replace(".", "");
|
||||
var markersArr = markers;
|
||||
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
|
||||
var nearbyArr = []
|
||||
var nearbyArr = [];
|
||||
if (snapshot.exists()) {
|
||||
var data = snapshot.val();
|
||||
for (var room in data) {
|
||||
@@ -138,6 +148,15 @@ function Home() {
|
||||
width={30}
|
||||
anchor={[data[room].latitude, data[room].longitude]}
|
||||
color="blue"
|
||||
onClick={() =>
|
||||
(window.location.href =
|
||||
"/app?room=" +
|
||||
data[room].path +
|
||||
"/" +
|
||||
data[room].name +
|
||||
"-" +
|
||||
data[room].timestamp)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -150,13 +169,13 @@ function Home() {
|
||||
});
|
||||
});
|
||||
}
|
||||
},[user]);
|
||||
}, [user]);
|
||||
|
||||
// Dont Double Send Leaving Message
|
||||
useEffect(() => {
|
||||
if (myRoomsObj && chatRoomObj) {
|
||||
var roomName = chatRoomObj.name + "-" + chatRoomObj.timestamp;
|
||||
if (myRooms != null && roomName in myRoomsObj) {
|
||||
if (roomData != null && roomName in myRoomsObj) {
|
||||
// its in there
|
||||
setIsMyRoom(true);
|
||||
} else {
|
||||
@@ -168,98 +187,98 @@ function Home() {
|
||||
|
||||
// Selects chat room
|
||||
function selectChatRoom(roomObj) {
|
||||
// Path of chatroom
|
||||
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
|
||||
// Path of chatroom
|
||||
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
|
||||
|
||||
setChatRoomObj(roomObj);
|
||||
setChatRoomObj(roomObj);
|
||||
|
||||
// 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
|
||||
);
|
||||
// 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
|
||||
);
|
||||
|
||||
// Code for Room Data
|
||||
set(ref(database, `/rooms/${path}/users/online/${user.uid}`), user);
|
||||
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
|
||||
setChatRoomOnline(null);
|
||||
setChatroomUsers(null);
|
||||
// Code for Room Data
|
||||
set(ref(database, `/rooms/${path}/users/online/${user.uid}`), user);
|
||||
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
|
||||
setChatroomOnline(null);
|
||||
setChatroomUsers(null);
|
||||
|
||||
// Active users list
|
||||
if (
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("online")
|
||||
) {
|
||||
var activeUsers = [];
|
||||
var activeUsersJSON = snapshot.val().users.online;
|
||||
for (var user in activeUsersJSON)
|
||||
activeUsers.push(<Member memberObj={activeUsersJSON[user]} />);
|
||||
setChatRoomOnline(activeUsers);
|
||||
}
|
||||
// Active users list
|
||||
if (
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("online")
|
||||
) {
|
||||
var activeUsers = [];
|
||||
var activeUsersJSON = snapshot.val().users.online;
|
||||
for (var user in activeUsersJSON)
|
||||
activeUsers.push(<Member memberObj={activeUsersJSON[user]} />);
|
||||
setChatroomOnline(activeUsers);
|
||||
}
|
||||
|
||||
// Users who added to "my rooms"
|
||||
if (
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("all")
|
||||
) {
|
||||
setChatroomUsersLoading(true);
|
||||
var allUsers = [];
|
||||
var allUsersJSON = snapshot.val().users.all;
|
||||
for (var user in allUsersJSON)
|
||||
allUsers.push(<Member memberObj={allUsersJSON[user]} />);
|
||||
setChatroomUsers(allUsers);
|
||||
setChatroomUsersLoading(false);
|
||||
}
|
||||
});
|
||||
setMainTab("chat");
|
||||
// Users who added to "my rooms"
|
||||
if (
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("all")
|
||||
) {
|
||||
setChatroomUsersLoading(true);
|
||||
var allUsers = [];
|
||||
var allUsersJSON = snapshot.val().users.all;
|
||||
for (var user in allUsersJSON)
|
||||
allUsers.push(<Member memberObj={allUsersJSON[user]} />);
|
||||
setChatroomUsers(allUsers);
|
||||
setChatroomUsersLoading(false);
|
||||
}
|
||||
});
|
||||
setMainTab("chat");
|
||||
}
|
||||
|
||||
// Fires to tell other uses that you are leaving the room
|
||||
useBeforeunload(() => {
|
||||
if (chatRoomObj && mainTab == "chat") {
|
||||
var payload = {
|
||||
body: "left",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
uid: user.uid
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp
|
||||
}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
remove(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp
|
||||
}/users/online/${userID}`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (chatRoomObj && mainTab == "chat") {
|
||||
var payload = {
|
||||
body: "left",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
uid: user.uid,
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp
|
||||
}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
remove(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp
|
||||
}/users/online/${userID}`
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -269,32 +288,54 @@ function Home() {
|
||||
{/* Left Side of Page */}
|
||||
<div className="col-span-3 h-dvh">
|
||||
{/* Header */}
|
||||
<Header mainTab={mainTab} isMyRoom={isMyRoom} chatRoomObj={chatRoomObj} setChatRoomObj={setChatRoomObj} setMainTab={setMainTab} setIsMyRoom={setIsMyRoom} user={user}/>
|
||||
<Header
|
||||
mainTab={mainTab}
|
||||
isMyRoom={isMyRoom}
|
||||
chatRoomObj={chatRoomObj}
|
||||
setChatRoomObj={setChatRoomObj}
|
||||
setMainTab={setMainTab}
|
||||
setIsMyRoom={setIsMyRoom}
|
||||
user={user}
|
||||
/>
|
||||
{/* Main Page Section */}
|
||||
<div className="mr-2 h-[calc(100%-110px)]">
|
||||
{mainTab == "home" && !loadingLoc && (
|
||||
<MainTabHome loc={location} markers={markers} user={user}/>
|
||||
<MainTabHome loc={location} markers={markers} user={user} />
|
||||
)}
|
||||
{mainTab == "home" && loadingLoc && (
|
||||
<MainTabHome loc={null} markers={markers} user={user}/>
|
||||
<MainTabHome loc={null} markers={markers} user={user} />
|
||||
)}
|
||||
{mainTab == "chat" && (
|
||||
<MainTabChatRoom roomObj={chatRoomObj} user={user} />
|
||||
)}
|
||||
{mainTab == "chat" && <MainTabChatRoom roomObj={chatRoomObj} user={user}/>}
|
||||
</div>
|
||||
</div>
|
||||
{/* Sidebar (Right Side of Page) */}
|
||||
{mainTab == "home" && (
|
||||
<Home_Sidebar tab={tab} nearby={nearby} loadingNearby={loadingNearby} setTab={setTab} isRoomLoading={isRoomLoading} myRooms={myRooms} loadingLoc={loadingLoc} location={location}/>
|
||||
<Home_Sidebar
|
||||
tab={tab}
|
||||
nearby={nearby}
|
||||
loadingNearby={loadingNearby}
|
||||
setTab={setTab}
|
||||
isRoomLoading={isRoomLoading}
|
||||
myRooms={roomData}
|
||||
loadingLoc={loadingLoc}
|
||||
location={location}
|
||||
/>
|
||||
)}
|
||||
{mainTab == "chat" && (
|
||||
<Chat_Sidebar chatRoomObj={chatRoomObj} chatroomOnline={chatroomOnline} chatroomUsersLoading={chatroomUsersLoading} chatroomUsers={chatroomUsers} setTab={setTab}/>
|
||||
)}
|
||||
{mainTab == "profile" && (
|
||||
<Profile_Sidebar/>
|
||||
<Chat_Sidebar
|
||||
chatRoomObj={chatRoomObj}
|
||||
chatroomOnline={chatroomOnline}
|
||||
chatroomUsersLoading={chatroomUsersLoading}
|
||||
chatroomUsers={chatroomUsers}
|
||||
/>
|
||||
)}
|
||||
{mainTab == "profile" && <Profile_Sidebar />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
export default Home;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
body {
|
||||
background-color: aliceblue;
|
||||
text-align: center;
|
||||
text-wrap:pretty;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
@@ -5,15 +5,13 @@ const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps",
|
||||
description: "ChatMaps: Social Media for College Students"
|
||||
description: "ChatMaps: Social Media for College Students",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ export const metadata = {
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,66 +1,122 @@
|
||||
"use client";
|
||||
import { useForm, Form } from "react-hook-form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import "../globals.css"
|
||||
import "../globals.css";
|
||||
|
||||
// Firebase imports
|
||||
import {auth} from "../../../firebase-config";
|
||||
import { setPersistence, signInWithEmailAndPassword, indexedDBLocalPersistence } from "firebase/auth";
|
||||
import { auth } from "../../../firebase-config";
|
||||
import {
|
||||
setPersistence,
|
||||
signInWithEmailAndPassword,
|
||||
indexedDBLocalPersistence,
|
||||
} from "firebase/auth";
|
||||
|
||||
function Login() {
|
||||
var router = useRouter();
|
||||
//var { register, handleSubmit } = useForm();
|
||||
var { register, control, setError, handleSubmit, formState: { errors, isSubmitting, isSubmitted } } = useForm()
|
||||
var router = useRouter();
|
||||
//var { register, handleSubmit } = useForm();
|
||||
var {
|
||||
register,
|
||||
control,
|
||||
setError,
|
||||
handleSubmit,
|
||||
formState: { errors, isSubmitting, isSubmitted },
|
||||
} = useForm();
|
||||
|
||||
function authenticate(data) {
|
||||
setPersistence(auth, indexedDBLocalPersistence)
|
||||
.then(() => {
|
||||
signInWithEmailAndPassword(auth,data.email,data.password)
|
||||
.then((userCredential) => {
|
||||
if (userCredential.user) {
|
||||
router.push("/app")
|
||||
} else {
|
||||
const formError = { type: "server", message: "Username or Password Incorrect" }
|
||||
// set same error in both:
|
||||
setError('password', formError)
|
||||
setError('email', formError)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
function authenticate(data) {
|
||||
setPersistence(auth, indexedDBLocalPersistence).then(() => {
|
||||
signInWithEmailAndPassword(auth, data.email, data.password).then(
|
||||
(userCredential) => {
|
||||
if (userCredential.user) {
|
||||
router.push("/app");
|
||||
} else {
|
||||
const formError = {
|
||||
type: "server",
|
||||
message: "Username or Password Incorrect",
|
||||
};
|
||||
// set same error in both:
|
||||
setError("password", formError);
|
||||
setError("email", formError);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<a href="/"><img src="logos/logo_transparent_inverse.png"/></a>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-[24px] mt-[25px] mb-2">Login</h3>
|
||||
{(errors.email && errors.password) && <div className="text-[red] mb-2 text-[18px] text-bold">Invalid Email or Password.</div>}
|
||||
<Form onSubmit={handleSubmit(authenticate)} encType={'application/json'}
|
||||
control={control}
|
||||
>
|
||||
<input type="email" id="email" className={(errors.email && errors.password) && "err"} {...register("email", { required: true })} placeholder="Enter Email Address"/><br/>
|
||||
<input type="password" id="password" name="password" className={(errors.email && errors.password) && "err"} {...register("password", { required: true })} placeholder="Enter Password"/><br/>
|
||||
<button type="submit" className="inline-flex items-center px-4 py-2 transition ease-in-out duration-150 bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
|
||||
{(isSubmitting || isSubmitted) && <span className="inline-block">
|
||||
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" strokeWidth="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</span> }
|
||||
Log In
|
||||
</button>
|
||||
<br/>Need an account? <a href="/register">Sign Up</a><br/>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/">
|
||||
<img src="logos/logo_transparent_inverse.png" />
|
||||
</a>
|
||||
<span className="text-[36px]">Chat with friends!</span>
|
||||
<div>
|
||||
<h3 className="text-[24px] mt-[25px] mb-2">Login</h3>
|
||||
{errors.email && errors.password && (
|
||||
<div className="text-[red] mb-2 text-[18px] text-bold">
|
||||
Invalid Email or Password.
|
||||
</div>
|
||||
)}
|
||||
<Form
|
||||
onSubmit={handleSubmit(authenticate)}
|
||||
encType={"application/json"}
|
||||
control={control}
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
className={errors.email && errors.password && "err"}
|
||||
{...register("email", { required: true })}
|
||||
placeholder="Enter Email Address"
|
||||
/>
|
||||
<br />
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
className={errors.email && errors.password && "err"}
|
||||
{...register("password", { required: true })}
|
||||
placeholder="Enter Password"
|
||||
/>
|
||||
<br />
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex items-center transition ease-in-out duration-150 m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full"
|
||||
>
|
||||
{(isSubmitting || isSubmitted) && (
|
||||
<span className="inline-block">
|
||||
<svg
|
||||
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
Log In
|
||||
</button>
|
||||
<br />
|
||||
Need an account? <a href="/register">Sign Up</a>
|
||||
<br />
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
||||
export default Login;
|
||||
|
||||
@@ -5,15 +5,13 @@ const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps: Onboarding",
|
||||
description: "ChatMaps: Social Media for College Students"
|
||||
description: "ChatMaps: Social Media for College Students",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,57 +1,75 @@
|
||||
"use client";
|
||||
import "../globals.css"
|
||||
import "../globals.css";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ref, set } from "firebase/database";
|
||||
import {auth, database} from "../../../firebase-config"
|
||||
import {onAuthStateChanged} from "firebase/auth"
|
||||
import { auth, database } from "../../../firebase-config";
|
||||
import { onAuthStateChanged } from "firebase/auth";
|
||||
|
||||
function createUser(data) {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user.uid) {
|
||||
data.uid = user.uid
|
||||
data.defined = true
|
||||
data.email = user.email
|
||||
set(ref(database, `users/${user.uid}`), data);
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user.uid) {
|
||||
data.uid = user.uid;
|
||||
data.defined = true;
|
||||
data.email = user.email;
|
||||
set(ref(database, `users/${user.uid}`), data);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Onboarding() {
|
||||
var router = useRouter();
|
||||
var { register, handleSubmit } = useForm();
|
||||
var router = useRouter();
|
||||
var { register, handleSubmit } = useForm();
|
||||
|
||||
function Onboard(data) {
|
||||
createUser(data)
|
||||
router.push("/app");
|
||||
|
||||
}
|
||||
return (
|
||||
function Onboard(data) {
|
||||
createUser(data);
|
||||
router.push("/app");
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<img src="logos/logo_transparent_inverse.png"/>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div className="m-5">
|
||||
Welcome to ChatMaps! We are excited to have you join our community!<br/>First we just need a little bit of information from you to get started.
|
||||
</div>
|
||||
<form action="#" onSubmit={handleSubmit(Onboard)}>
|
||||
<input type="text" {...register("username")} placeholder="Display Name"/><br/>
|
||||
<input type="text" {...register("firstName")} placeholder="First Name"/><br/>
|
||||
<input type="text" {...register("lastName")} placeholder="Last Name"/><br/>
|
||||
<button type="submit" className="inline-flex items-center px-4 py-2 transition ease-in-out duration-150 bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<img src="logos/logo_transparent_inverse.png" />
|
||||
<span className="text-[36px]">Chat with friends!</span>
|
||||
<div className="m-5">
|
||||
Welcome to ChatMaps! We are excited to have you join our community!
|
||||
<br />
|
||||
First we just need a little bit of information from you to get
|
||||
started.
|
||||
</div>
|
||||
<form action="#" onSubmit={handleSubmit(Onboard)}>
|
||||
<input
|
||||
type="text"
|
||||
{...register("username")}
|
||||
placeholder="Display Name"
|
||||
/>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
{...register("firstName")}
|
||||
placeholder="First Name"
|
||||
/>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
{...register("lastName")}
|
||||
placeholder="Last Name"
|
||||
/>
|
||||
<br />
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex items-center transition ease-in-out duration-150 m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Onboarding;
|
||||
export default Onboarding;
|
||||
|
||||
@@ -1,73 +1,99 @@
|
||||
"use client"
|
||||
import { useState, useEffect } from 'react'
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { auth, database } from "../../firebase-config";
|
||||
import { ref, get} from "firebase/database";
|
||||
import {onAuthStateChanged} from "firebase/auth"
|
||||
|
||||
import { ref, get } from "firebase/database";
|
||||
import { onAuthStateChanged } from "firebase/auth";
|
||||
|
||||
function Home() {
|
||||
const [isLoadingLoc, setLoadingLoc] = useState(true)
|
||||
const [roomCount, setRoomCount] = useState(null)
|
||||
const [isAuthenticated, setAuth] = useState(false)
|
||||
const [userID, setUserID] = useState(null)
|
||||
const [isLoadingLoc, setLoadingLoc] = useState(true);
|
||||
const [roomCount, setRoomCount] = useState(null);
|
||||
const [isAuthenticated, setAuth] = useState(false);
|
||||
const [userID, setUserID] = useState(null);
|
||||
|
||||
// Authentication
|
||||
// Authentication
|
||||
useEffect(() => {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user) {
|
||||
setUserID(user.uid)
|
||||
setAuth(true)
|
||||
setUserID(user.uid);
|
||||
setAuth(true);
|
||||
} else {
|
||||
setAuth(false)
|
||||
setAuth(false);
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if('geolocation' in navigator) {
|
||||
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
|
||||
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
||||
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
|
||||
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
|
||||
if (snapshot.exists()) {
|
||||
var count = 0
|
||||
for (var room in snapshot.val()) {
|
||||
count += 1
|
||||
}
|
||||
setRoomCount(count)
|
||||
} else {
|
||||
setRoomCount(0)
|
||||
}
|
||||
setLoadingLoc(false)
|
||||
});
|
||||
});
|
||||
useEffect(() => {
|
||||
if ("geolocation" in navigator) {
|
||||
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
|
||||
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
||||
var path =
|
||||
String(coords.latitude.toFixed(2)).replace(".", "") +
|
||||
"/" +
|
||||
String(coords.longitude.toFixed(2)).replace(".", "");
|
||||
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
|
||||
if (snapshot.exists()) {
|
||||
var count = 0;
|
||||
for (var room in snapshot.val()) {
|
||||
count += 1;
|
||||
}
|
||||
setRoomCount(count);
|
||||
} else {
|
||||
setRoomCount(0);
|
||||
}
|
||||
})
|
||||
return (
|
||||
setLoadingLoc(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<img src="logos/logo_transparent_inverse.png"/>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div className="m-5">
|
||||
{(!isAuthenticated) &&
|
||||
<div>
|
||||
<a href="/login"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Login</button></a>
|
||||
<a href="/register"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Sign Up</button></a>
|
||||
</div>
|
||||
}
|
||||
{isAuthenticated && <a href="/app"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Continue to App</button></a>}
|
||||
{(!isLoadingLoc && roomCount == 1) && <div className='text-[24px] pt-10'>Join others in {roomCount} room near you!</div>}
|
||||
{(!isLoadingLoc && roomCount != 1 && roomCount != 0) && <div className='text-[24px] pt-10'>Join others in {roomCount} rooms near you!</div>}
|
||||
{(isLoadingLoc || (roomCount == 0 && !isLoadingLoc)) && <div className='text-[24px] pt-10'>Start the conversation today!</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<img src="logos/logo_transparent_inverse.png" />
|
||||
<span className="text-[36px]">Chat with friends!</span>
|
||||
<div className="m-5">
|
||||
{!isAuthenticated && (
|
||||
<div>
|
||||
<a href="/login">
|
||||
<button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
|
||||
Login
|
||||
</button>
|
||||
</a>
|
||||
<a href="/register">
|
||||
<button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
|
||||
Sign Up
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
{isAuthenticated && (
|
||||
<a href="/app">
|
||||
<button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
|
||||
Continue to App
|
||||
</button>
|
||||
</a>
|
||||
)}
|
||||
{!isLoadingLoc && roomCount == 1 && (
|
||||
<div className="text-[24px] pt-10">
|
||||
Join others in {roomCount} room near you!
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingLoc && roomCount != 1 && roomCount != 0 && (
|
||||
<div className="text-[24px] pt-10">
|
||||
Join others in {roomCount} rooms near you!
|
||||
</div>
|
||||
)}
|
||||
{(isLoadingLoc || (roomCount == 0 && !isLoadingLoc)) && (
|
||||
<div className="text-[24px] pt-10">
|
||||
Start the conversation today!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
export default Home;
|
||||
|
||||
@@ -11,9 +11,7 @@ export const metadata = {
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,73 +1,122 @@
|
||||
"use client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm, Form } from "react-hook-form";
|
||||
import "../globals.css"
|
||||
import "../globals.css";
|
||||
import { useState } from "react";
|
||||
|
||||
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, setPersistence, indexedDBLocalPersistence } from "firebase/auth";
|
||||
import {auth} from "../../../firebase-config";
|
||||
import {
|
||||
createUserWithEmailAndPassword,
|
||||
signInWithEmailAndPassword,
|
||||
setPersistence,
|
||||
indexedDBLocalPersistence,
|
||||
} from "firebase/auth";
|
||||
import { auth } from "../../../firebase-config";
|
||||
|
||||
async function Signup(data) {
|
||||
var userCredential = await createUserWithEmailAndPassword(auth,data.email,data.password);
|
||||
if (userCredential.user) {
|
||||
setPersistence(auth, indexedDBLocalPersistence )
|
||||
.then(() => {
|
||||
signInWithEmailAndPassword(auth,data.email,data.password)
|
||||
.then((res) => {
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
var userCredential = await createUserWithEmailAndPassword(
|
||||
auth,
|
||||
data.email,
|
||||
data.password
|
||||
);
|
||||
if (userCredential.user) {
|
||||
setPersistence(auth, indexedDBLocalPersistence).then(() => {
|
||||
signInWithEmailAndPassword(auth, data.email, data.password).then(
|
||||
(res) => {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function Register() {
|
||||
var router = useRouter();
|
||||
var { register, control, formState: { errors } } = useForm()
|
||||
var emailRegex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
|
||||
var [passwordMismatch, setPasswordMismatch] = useState(false);
|
||||
const passwordMatch = (data) => {
|
||||
return data.password === data.passwordCheck;
|
||||
};
|
||||
var router = useRouter();
|
||||
var {
|
||||
register,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm();
|
||||
var emailRegex =
|
||||
/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
|
||||
var [passwordMismatch, setPasswordMismatch] = useState(false);
|
||||
const passwordMatch = (data) => {
|
||||
return data.password === data.passwordCheck;
|
||||
};
|
||||
|
||||
function onSubmit({data}) {
|
||||
if (passwordMatch(data)) {
|
||||
setPasswordMismatch(false);
|
||||
if (Signup(data)) {
|
||||
router.push("/onboarding");
|
||||
}
|
||||
|
||||
} else{
|
||||
setPasswordMismatch(true);
|
||||
return;
|
||||
}
|
||||
function onSubmit({ data }) {
|
||||
if (passwordMatch(data)) {
|
||||
setPasswordMismatch(false);
|
||||
if (Signup(data)) {
|
||||
router.push("/onboarding");
|
||||
}
|
||||
} else {
|
||||
setPasswordMismatch(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<a href="/"><img src="logos/logo_transparent_inverse.png"/></a>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-[24px] mt-[15px]">Register</h3>
|
||||
<Form onSubmit={onSubmit} encType={'application/json'} control={control}>
|
||||
<input type="email" {...register("email", {required: true, pattern: emailRegex})} className={errors.email && "err"} placeholder="Enter Email Address"/><br/>
|
||||
<input type="password" {...register("password", {required: true})} className={errors.password && errors.password.type == 'required' && "err"} placeholder="Enter Password"/><br/>
|
||||
<input type ="password" {...register("passwordCheck", {required: false})} className ={errors.passwordCheck && errors.passwordCheck.type == 'required' && "err"} placeholder="Re-enter Password"/><br/>
|
||||
{passwordMismatch && <p className="text-red-500">Passwords do not match</p>}
|
||||
<button type="submit" className="bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full"> Register</button><br/>
|
||||
Have an account? <a href="/login">Log In</a>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/">
|
||||
<img src="logos/logo_transparent_inverse.png" />
|
||||
</a>
|
||||
<span className="text-[36px]">Chat with friends!</span>
|
||||
<div>
|
||||
<h3 className="text-[24px] mt-[15px]">Register</h3>
|
||||
<Form
|
||||
onSubmit={onSubmit}
|
||||
encType={"application/json"}
|
||||
control={control}
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
{...register("email", { required: true, pattern: emailRegex })}
|
||||
className={errors.email && "err"}
|
||||
placeholder="Enter Email Address"
|
||||
/>
|
||||
<br />
|
||||
<input
|
||||
type="password"
|
||||
{...register("password", { required: true })}
|
||||
className={
|
||||
errors.password && errors.password.type == "required" && "err"
|
||||
}
|
||||
placeholder="Enter Password"
|
||||
/>
|
||||
<br />
|
||||
<input
|
||||
type="password"
|
||||
{...register("passwordCheck", { required: false })}
|
||||
className={
|
||||
errors.passwordCheck &&
|
||||
errors.passwordCheck.type == "required" &&
|
||||
"err"
|
||||
}
|
||||
placeholder="Re-enter Password"
|
||||
/>
|
||||
<br />
|
||||
{passwordMismatch && (
|
||||
<p className="text-red-500">Passwords do not match</p>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className=" m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full"
|
||||
>
|
||||
{" "}
|
||||
Register
|
||||
</button>
|
||||
<br />
|
||||
Have an account? <a href="/login">Log In</a>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Register;
|
||||
|
||||
@@ -11,9 +11,7 @@ export const metadata = {
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,108 +2,89 @@
|
||||
// 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";
|
||||
import { ref, onValue, get, update } from "firebase/database";
|
||||
import { onAuthStateChanged } from "firebase/auth";
|
||||
|
||||
// Refactored Component Imports
|
||||
|
||||
// Data Structure Imports
|
||||
import { Interest, ProfileRoom } from "../../../components/app/datatypes";
|
||||
import { ProfileRoom } from "../../../components/app/profile/ProfileRoom";
|
||||
import { ProfileEdit } from "../../../components/app/profile/ProfileEdit";
|
||||
import { Interest } from "../../../components/app/profile/Interest";
|
||||
|
||||
// Header Import
|
||||
import { Header } from "../../../components/app/header";
|
||||
import { uploadBytes } from "firebase/storage";
|
||||
|
||||
// Contains most everything for the app homepage
|
||||
function Home({ params }) {
|
||||
// User Profile Page
|
||||
function UserProfile({ 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)
|
||||
const [profileData, setProfileData] = useState(null);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [user, setUser] = useState(null);
|
||||
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
|
||||
|
||||
var { register, control} = useForm()
|
||||
// Handles Edit State in Component, shares useState with ProfileEdit
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const handleIsEditing = (newValue) => {
|
||||
setIsEditing(newValue);
|
||||
};
|
||||
|
||||
// Authentication
|
||||
useEffect(() => {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user) {
|
||||
get(ref(database, `users/${user.uid}`))
|
||||
.then((userData) => {
|
||||
userData = userData.val()
|
||||
get(ref(database, `users/${user.uid}`)).then((userData) => {
|
||||
userData = userData.val();
|
||||
if (userData) {
|
||||
if (userData.uid == params.stub) {
|
||||
setOwn(true)
|
||||
setIsOwner(true);
|
||||
}
|
||||
setUser(userData)
|
||||
setAuth(true)
|
||||
setUser(userData);
|
||||
setIsAuthenticated(true);
|
||||
} else {
|
||||
window.location.href = "/onboarding"
|
||||
window.location.href = "/onboarding";
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
} else {
|
||||
setAuth(false)
|
||||
window.location.href = "/login"
|
||||
setIsAuthenticated(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(<Interest interest={interests[interest]}/>)
|
||||
i++;
|
||||
}
|
||||
setUserInterestArray(interestArray)
|
||||
var rooms = snapshot.val().rooms;
|
||||
var roomArray = []
|
||||
for (var room in rooms) {
|
||||
roomArray.push(<ProfileRoom room={rooms[room]}/>)
|
||||
}
|
||||
setUserRoomsArray(roomArray);
|
||||
});
|
||||
},[]);
|
||||
onValue(ref(database, "/users/" + params.stub), (snapshot) => {
|
||||
setProfileData(snapshot.val());
|
||||
|
||||
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]
|
||||
}
|
||||
// Populates array with user's interests
|
||||
var interests = snapshot.val().interests || null;
|
||||
if (interests == null) {
|
||||
// Placeholder for no interests specified, will be replaced with default interests
|
||||
interests = "Music, Sports, Movies";
|
||||
}
|
||||
data.pfp = profileData.pfp
|
||||
setEdit(false)
|
||||
update(ref(database, `users/${user.uid}`), data)
|
||||
}
|
||||
}
|
||||
interests = interests.split(",");
|
||||
var interestArray = [];
|
||||
var i = 0;
|
||||
for (var interest in interests) {
|
||||
if (i < 4)
|
||||
interestArray.push(<Interest interest={interests[interest]} />);
|
||||
i++;
|
||||
}
|
||||
setUserInterestArray(interestArray);
|
||||
|
||||
// Populates array with user's rooms
|
||||
var rooms = snapshot.val().rooms;
|
||||
var roomArray = [];
|
||||
for (var room in rooms) {
|
||||
roomArray.push(<ProfileRoom room={rooms[room]} />);
|
||||
}
|
||||
setUserRoomsArray(roomArray);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -112,67 +93,50 @@ function Home({ params }) {
|
||||
{/* Left Side of Page */}
|
||||
<div className="h-dvh overflow-hidden">
|
||||
{/* Header */}
|
||||
<Header user={user}/>
|
||||
<Header user={user} />
|
||||
{/* Main Page Section */}
|
||||
<div className="grid grid-cols-3 mr-2 h-[calc(100%-110px)] pl-5 pr-5 pt-2">
|
||||
|
||||
<div className="cols-span-1 bg-white shadow-2xl rounded-xl pt-5">
|
||||
{!isEditing && (
|
||||
<div>
|
||||
<img src={profileData.pfp} width="300px" className="relative mx-auto rounded-2xl overflow-hidden"/>
|
||||
<img
|
||||
src={profileData.pfp}
|
||||
width="300px"
|
||||
className="relative mx-auto rounded-2xl overflow-hidden"
|
||||
/>
|
||||
<div className="font-bold text-[30px]">
|
||||
{profileData.firstName} {profileData.lastName}
|
||||
{profileData.firstName} {profileData.lastName}
|
||||
</div>
|
||||
<div className="text-[20px]">@{profileData.username}</div>
|
||||
<div className="pt-5">{profileData.bio}</div>
|
||||
<div className="grid grid-cols-3 p-3">
|
||||
{userInterestArray}
|
||||
{userInterestArray}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 auto-cols-min justify-items-center">
|
||||
{isOwner && ( <a onClick={() => {setEdit(true)}} className="w-[120px] p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full text-center"> Edit Profile </a> )}
|
||||
{!isOwner && ( <a className="w-[120px] p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full text-center"> Add Friend </a> )}
|
||||
{isOwner && (
|
||||
<a
|
||||
onClick={() => {
|
||||
setIsEditing(true);
|
||||
}}
|
||||
className="w-[120px] p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full text-center"
|
||||
>
|
||||
Edit Profile{" "}
|
||||
</a>
|
||||
)}
|
||||
{!isOwner && (
|
||||
<a className="w-[120px] p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full text-center">
|
||||
Add Friend{" "}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isEditing && (
|
||||
<div>
|
||||
<Form onSubmit={save} encType={'application/json'} control={control}>
|
||||
<div className="grid grid-cols-2">
|
||||
<div>
|
||||
<img src={profileData.pfp} width="150px" className="relative mx-auto rounded-2xl overflow-hidden"/>
|
||||
Current Profile Picture
|
||||
</div>
|
||||
<div className="flex content-center">
|
||||
<input type="file" {...register("pfp")} className="w-[80%]" accept=".jpg,.png,.jpeg"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 pl-2 w-[90%]">
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">First Name</div>
|
||||
<input className="w-[80%] border-2 border-gray-300 p-2 rounded-lg" type="text" {...register("firstName")} placeholder={profileData.firstName}/>
|
||||
</div>
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">Last Name</div>
|
||||
<input className="w-[80%] border-2 border-gray-300 p-2 rounded-lg" type="text" {...register("lastName")} placeholder={profileData.lastName}/>
|
||||
</div>
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">Username</div>
|
||||
<input className="w-[80%] border-2 border-gray-300 p-2 rounded-lg" type="text" {...register("username")} placeholder={profileData.username}/>
|
||||
</div>
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">Interests (Comma Seperated)</div>
|
||||
<input className="w-[80%] border-2 border-gray-300 p-2 rounded-lg" type="text" {...register("interests")} placeholder={profileData.interests}/>
|
||||
</div>
|
||||
<div className="pt-5 col-span-2">
|
||||
<div className="font-bold">Bio</div>
|
||||
<textarea className="w-[92%] border-2 border-gray-300 p-2 rounded-lg" {...register("bio")} type="text" placeholder={profileData.bio}/>
|
||||
</div>
|
||||
<div className="justify-items-center pt-5 col-span-2">
|
||||
<button type="submit" className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full text-center"> Save Changes </button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<ProfileEdit
|
||||
profileData={profileData}
|
||||
user={user}
|
||||
onSave={handleIsEditing}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
@@ -184,8 +148,8 @@ function Home({ params }) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
export default UserProfile;
|
||||
|
||||
@@ -1,31 +1,64 @@
|
||||
import { Map, Marker, ZoomControl } from "pigeon-maps";
|
||||
|
||||
// Colors for Messages
|
||||
const userColors = [
|
||||
"#ff80ed",
|
||||
"#065535",
|
||||
"#133337",
|
||||
"#ffc0cb",
|
||||
"#e6e6fa",
|
||||
"#ffd700",
|
||||
"#ffa500",
|
||||
"#0000ff",
|
||||
"#1b85b8",
|
||||
"#5a5255",
|
||||
"#559e83",
|
||||
"#ae5a41",
|
||||
"#c3cb71",
|
||||
];
|
||||
"#ff80ed",
|
||||
"#065535",
|
||||
"#133337",
|
||||
"#ffc0cb",
|
||||
"#e6e6fa",
|
||||
"#ffd700",
|
||||
"#ffa500",
|
||||
"#0000ff",
|
||||
"#1b85b8",
|
||||
"#5a5255",
|
||||
"#559e83",
|
||||
"#ae5a41",
|
||||
"#c3cb71",
|
||||
];
|
||||
|
||||
let dateOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
};
|
||||
let dateOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
};
|
||||
|
||||
const generateColor = (user_str) => {
|
||||
// hashes username for consistent colors, maybe all functionality to pick color later
|
||||
let hash = 0;
|
||||
for (let i = 0; i < user_str.length; i++) {
|
||||
hash = user_str.charCodeAt(i) + (hash * 32 - hash);
|
||||
}
|
||||
const index = Math.abs(hash) % userColors.length;
|
||||
return index;
|
||||
};
|
||||
|
||||
// Chat Message
|
||||
export function Chat({ chatObj }) {
|
||||
return (
|
||||
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
|
||||
<div>
|
||||
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
|
||||
<a
|
||||
href={chatObj.uid && "/user/" + chatObj.uid}
|
||||
className="hover:font-bold cursor-pointer"
|
||||
target="_blank"
|
||||
>
|
||||
{chatObj.user}
|
||||
</a>
|
||||
</span>
|
||||
: {chatObj.body}
|
||||
</div>
|
||||
<div className="text-right text-[#d1d1d1]">
|
||||
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// System Chat Message
|
||||
export function SystemMessage({ chatObj }) {
|
||||
const generateColor = (user_str) => {
|
||||
// hashes username for consistent colors, maybe all functionality to pick color later
|
||||
let hash = 0;
|
||||
@@ -36,129 +69,56 @@ const userColors = [
|
||||
return index;
|
||||
};
|
||||
|
||||
// Chat Message
|
||||
export function Chat({ chatObj }) {
|
||||
return (
|
||||
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
|
||||
<div>
|
||||
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
|
||||
<a href={chatObj.uid && ("/user/"+chatObj.uid)} className="hover:font-bold cursor-pointer" target="_blank">{chatObj.user}</a>
|
||||
</span>
|
||||
: {chatObj.body}
|
||||
</div>
|
||||
<div className="text-right text-[#d1d1d1]">
|
||||
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// System Chat Message
|
||||
export function SystemMessage({ chatObj }) {
|
||||
|
||||
const generateColor = (user_str) => {
|
||||
// hashes username for consistent colors, maybe all functionality to pick color later
|
||||
let hash = 0;
|
||||
for (let i = 0; i < user_str.length; i++) {
|
||||
hash = user_str.charCodeAt(i) + (hash * 32 - hash);
|
||||
}
|
||||
const index = Math.abs(hash) % userColors.length;
|
||||
return index;
|
||||
};
|
||||
|
||||
return (
|
||||
return (
|
||||
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
|
||||
<div className="text-[#d1d1d1]">
|
||||
<div className="text-[#d1d1d1]">
|
||||
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
|
||||
<a href={chatObj.uid && ("/user/"+chatObj.uid)} className="hover:font-bold cursor-pointer" target="_blank">{chatObj.user}</a>
|
||||
<a
|
||||
href={chatObj.uid && "/user/" + chatObj.uid}
|
||||
className="hover:font-bold cursor-pointer"
|
||||
target="_blank"
|
||||
>
|
||||
{chatObj.user}
|
||||
</a>
|
||||
</span>{" "}
|
||||
has {chatObj.body} the room.
|
||||
</div>
|
||||
<div className="text-right text-[#d1d1d1]">
|
||||
</div>
|
||||
<div className="text-right text-[#d1d1d1]">
|
||||
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Member for Active/Room members in sidebar
|
||||
export function Member({ memberObj }) {
|
||||
return (
|
||||
<a href={"/user/"+memberObj.uid} target="_blank">
|
||||
<div className="cursor-pointer g-[aliceblue] rounded-lg m-3 shadow-xl p-2" >
|
||||
return (
|
||||
<a href={"/user/" + memberObj.uid} target="_blank">
|
||||
<div className="cursor-pointer g-[aliceblue] rounded-lg m-3 shadow-xl p-2">
|
||||
{memberObj.username}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Chat Room Object for myRooms and Nearby in sidebar
|
||||
export function ChatRoomSidebar({ roomObj, click }) {
|
||||
// TODO: Gross fix but it works
|
||||
function clicker() {
|
||||
click(roomObj);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onClick={clicker}
|
||||
className="border-[black] border-1 shadow-lg p-2 m-2 rounded-lg cursor-pointer"
|
||||
>
|
||||
<div className="col-span-2">
|
||||
<div className="font-bold">{roomObj.name}</div>
|
||||
<div className="italic">{roomObj.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// TODO: Gross fix but it works
|
||||
function clicker() {
|
||||
click(roomObj);
|
||||
}
|
||||
|
||||
// Map module for main page and chat room sidebar
|
||||
// TODO: MAKE NOT MOVABLE
|
||||
export function Geo({ loc, zoom, locMarker, markers }) {
|
||||
if (loc) {
|
||||
return (
|
||||
<Map center={[loc.latitude, loc.longitude]} defaultZoom={zoom}>
|
||||
{markers && markers}
|
||||
{locMarker && (
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[loc.latitude, loc.longitude]}
|
||||
color="red"
|
||||
/>
|
||||
)}
|
||||
{zoom && <ZoomControl />}
|
||||
</Map>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Map className="rounded-lg" defaultCenter={[0, 0]} defaultZoom={zoom} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Interest for Profile
|
||||
export function Interest({interest}) {
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-lg m-2 p-2 shadow-xl">
|
||||
{interest}
|
||||
<div
|
||||
onClick={clicker}
|
||||
className="border-[black] border-1 shadow-lg p-2 m-2 rounded-lg cursor-pointer"
|
||||
>
|
||||
<div className="col-span-2">
|
||||
<div className="font-bold">{roomObj.name}</div>
|
||||
<div className="italic">{roomObj.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function ProfileRoom({room}) {
|
||||
return (
|
||||
<div className="rounded-lg p-2 shadow-xl bg-white h-[250px] w-[325px]">
|
||||
<div className="relative z-1 h-[235px] opacity-50">
|
||||
<Geo loc={{"latitude": room.latitude, "longitude": room.longitude}} zoom={12} locMarker={false}/>
|
||||
</div>
|
||||
<div className="relative z-2 top-[-235px] text-left p-2">
|
||||
<div className="text-2xl font-bold">{room.name}</div>
|
||||
<div>{room.description}</div>
|
||||
<div>Created on {new Date(room.timestamp).toLocaleString(dateOptions)}</div>
|
||||
<a href={"/app?room="+room.path+"/"+room.name+"-"+room.timestamp} className="absolute z-2 top-[190px] w-[108px] p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full flex items-center">Open Room</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// This will be removed once dateOptions is no longer used in this file
|
||||
export { dateOptions };
|
||||
|
||||
@@ -1,144 +1,153 @@
|
||||
import { auth, database } from "../../../firebase-config";
|
||||
import { ref, set, remove } from "firebase/database";
|
||||
import {signOut} from "firebase/auth";
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { signOut } from "firebase/auth";
|
||||
import { Popover } from "@headlessui/react";
|
||||
|
||||
|
||||
function logout() {
|
||||
signOut(auth)
|
||||
}
|
||||
function logout() {
|
||||
signOut(auth);
|
||||
}
|
||||
|
||||
// Closes chat room
|
||||
function closeChatRoom(roomObj, setChatRoomObj, setMainTab, user) {
|
||||
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
|
||||
var payload = {
|
||||
body: "left",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
uid: user.uid
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${path}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
remove(ref(database, `/rooms/${path}/users/online/${user.uid}`));
|
||||
setChatRoomObj(null);
|
||||
setMainTab("home");
|
||||
}
|
||||
// Closes chat room
|
||||
function closeChatRoom(roomObj, setChatRoomObj, setMainTab, user) {
|
||||
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
|
||||
var payload = {
|
||||
body: "left",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
uid: user.uid,
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${path}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
remove(ref(database, `/rooms/${path}/users/online/${user.uid}`));
|
||||
setChatRoomObj(null);
|
||||
setMainTab("home");
|
||||
}
|
||||
|
||||
// Adds room to myRooms
|
||||
function addToMyRooms(chatRoomObj, setIsMyRoom, user) {
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
|
||||
),
|
||||
{
|
||||
name: chatRoomObj.name,
|
||||
path: chatRoomObj.path,
|
||||
timestamp: chatRoomObj.timestamp,
|
||||
description: chatRoomObj.description,
|
||||
longitude: chatRoomObj.longitude,
|
||||
latitude: chatRoomObj.latitude,
|
||||
}
|
||||
);
|
||||
var path =
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp;
|
||||
set(ref(database, `/rooms/${path}/users/all/${user.uid}`), user);
|
||||
setIsMyRoom(true);
|
||||
}
|
||||
// Adds room to myRooms
|
||||
function addToMyRooms(chatRoomObj, setIsMyRoom, user) {
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
|
||||
),
|
||||
{
|
||||
name: chatRoomObj.name,
|
||||
path: chatRoomObj.path,
|
||||
timestamp: chatRoomObj.timestamp,
|
||||
description: chatRoomObj.description,
|
||||
longitude: chatRoomObj.longitude,
|
||||
latitude: chatRoomObj.latitude,
|
||||
}
|
||||
);
|
||||
var path =
|
||||
chatRoomObj.path + "/" + chatRoomObj.name + "-" + chatRoomObj.timestamp;
|
||||
set(ref(database, `/rooms/${path}/users/all/${user.uid}`), user);
|
||||
setIsMyRoom(true);
|
||||
}
|
||||
|
||||
// Deletes saved room from myRooms
|
||||
function removeFromMyRooms(chatRoomObj, setIsMyRoom, user) {
|
||||
var path =
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp;
|
||||
remove(
|
||||
ref(
|
||||
database,
|
||||
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
|
||||
)
|
||||
);
|
||||
remove(ref(database, `/rooms/${path}/users/all/${user.uid}`));
|
||||
setIsMyRoom(false);
|
||||
}
|
||||
|
||||
export function Header({mainTab, isMyRoom, chatRoomObj, setChatRoomObj, setMainTab, setIsMyRoom, user}) {
|
||||
return (
|
||||
<div className="flex m-2 rounded-lg h-[63px] bg-white shadow-2xl p-1">
|
||||
<div className="flex shrink h-[60px]">
|
||||
<a href="/app">
|
||||
<img
|
||||
src="/logos/logo_transparent_inverse.png"
|
||||
className="h-[60px]"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div className="grow grid grid-rows-1 grid-flow-col auto-cols-max justify-end gap-2 h-[60px] p-2">
|
||||
{mainTab == "chat" && isMyRoom == false && (
|
||||
<a
|
||||
onClick={() => {
|
||||
addToMyRooms(chatRoomObj, setIsMyRoom, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
|
||||
>
|
||||
Add to "My Rooms"
|
||||
</a>
|
||||
)}
|
||||
{mainTab == "chat" && isMyRoom == true && (
|
||||
<a
|
||||
onClick={() => {
|
||||
removeFromMyRooms(chatRoomObj, setIsMyRoom, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
|
||||
>
|
||||
Remove from "My Rooms"
|
||||
</a>
|
||||
)}
|
||||
{mainTab == "chat" && (
|
||||
<a
|
||||
onClick={() => {
|
||||
closeChatRoom(chatRoomObj, setChatRoomObj, setMainTab, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
|
||||
>
|
||||
Close Chat
|
||||
</a>
|
||||
)}
|
||||
|
||||
<Popover className="relative">
|
||||
<Popover.Button as="div">
|
||||
<div className="mr-5 h-[44px] p-[2px] pr-[15px] cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full shadow-2xl flex">
|
||||
<div className="flex items-center pl-1">
|
||||
Nicholas
|
||||
</div>
|
||||
<div className="ml-3 rounded-lg">
|
||||
<img src={user.pfp} width="40px" className="relative mx-auto rounded-xl overflow-hidden"/>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Button>
|
||||
|
||||
<Popover.Panel className="absolute z-10 bg-white mt-[4px] rounded-xl ml-3 shadow-2xl">
|
||||
<div className="grid grid-cols-1">
|
||||
<a className="rounded-xl p-4 hover:bg-[#C0C0C0]" href={"/user/"+user.uid}>View Profile</a>
|
||||
<a className="rounded-xl p-4 hover:bg-[#C0C0C0]" onClick={logout} href="/">Sign Out</a>
|
||||
</div>
|
||||
|
||||
<img src="/solutions.jpg" alt="" />
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
// Deletes saved room from myRooms
|
||||
function removeFromMyRooms(chatRoomObj, setIsMyRoom, user) {
|
||||
var path =
|
||||
chatRoomObj.path + "/" + chatRoomObj.name + "-" + chatRoomObj.timestamp;
|
||||
remove(
|
||||
ref(
|
||||
database,
|
||||
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
|
||||
)
|
||||
}
|
||||
);
|
||||
remove(ref(database, `/rooms/${path}/users/all/${user.uid}`));
|
||||
setIsMyRoom(false);
|
||||
}
|
||||
|
||||
export function Header({
|
||||
mainTab,
|
||||
isMyRoom,
|
||||
chatRoomObj,
|
||||
setChatRoomObj,
|
||||
setMainTab,
|
||||
setIsMyRoom,
|
||||
user,
|
||||
}) {
|
||||
return (
|
||||
<div className="flex m-2 rounded-lg h-[63px] bg-white shadow-2xl p-1">
|
||||
<div className="flex shrink h-[60px]">
|
||||
<a href="/app">
|
||||
<img src="/logos/logo_transparent_inverse.png" className="h-[60px]" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="grow grid grid-rows-1 grid-flow-col auto-cols-max justify-end gap-2 h-[60px] p-2">
|
||||
{mainTab == "chat" && isMyRoom == false && (
|
||||
<a
|
||||
onClick={() => {
|
||||
addToMyRooms(chatRoomObj, setIsMyRoom, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
|
||||
>
|
||||
Add to "My Rooms"
|
||||
</a>
|
||||
)}
|
||||
{mainTab == "chat" && isMyRoom == true && (
|
||||
<a
|
||||
onClick={() => {
|
||||
removeFromMyRooms(chatRoomObj, setIsMyRoom, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
|
||||
>
|
||||
Remove from "My Rooms"
|
||||
</a>
|
||||
)}
|
||||
{mainTab == "chat" && (
|
||||
<a
|
||||
onClick={() => {
|
||||
closeChatRoom(chatRoomObj, setChatRoomObj, setMainTab, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
|
||||
>
|
||||
Close Chat
|
||||
</a>
|
||||
)}
|
||||
|
||||
<Popover className="relative">
|
||||
<Popover.Button as="div">
|
||||
<div className="mr-5 h-[44px] p-[2px] pr-[15px] cursor-pointer bg-cyan-500 text-white font-bold rounded-full shadow-2xl flex">
|
||||
<div className="flex items-center pl-1">{user.firstName}</div>
|
||||
<div className="ml-3 rounded-lg">
|
||||
<img
|
||||
src={user.pfp}
|
||||
width="40px"
|
||||
className="relative mx-auto rounded-xl overflow-hidden"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Button>
|
||||
|
||||
<Popover.Panel className="absolute z-10 bg-white mt-[4px] rounded-xl ml-3 shadow-2xl">
|
||||
<div className="grid grid-cols-1">
|
||||
<a
|
||||
className="rounded-xl p-4 hover:bg-[#C0C0C0]"
|
||||
href={"/user/" + user.uid}
|
||||
>
|
||||
View Profile
|
||||
</a>
|
||||
<a
|
||||
className="rounded-xl p-4 hover:bg-[#C0C0C0]"
|
||||
onClick={logout}
|
||||
href="/"
|
||||
>
|
||||
Sign Out
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<img src="/solutions.jpg" alt="" />
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,95 +1,94 @@
|
||||
import { Chat, SystemMessage} from "../datatypes"
|
||||
import { Chat, SystemMessage } from "../datatypes";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Form, useForm } from "react-hook-form";
|
||||
import { ref, onValue, set} from "firebase/database";
|
||||
import { ref, onValue, set } from "firebase/database";
|
||||
import { database } from "../../../../firebase-config";
|
||||
|
||||
|
||||
// Chatroom Module for Primary Tab
|
||||
// Chatroom Module for Primary Tab
|
||||
export function MainTabChatRoom({ roomObj, user }) {
|
||||
var { register, control, reset, handleSubmit } = useForm();
|
||||
const [chats, setData] = useState(null);
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
|
||||
// Message updater
|
||||
useEffect(() => {
|
||||
onValue(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
|
||||
}/chats`
|
||||
),
|
||||
(snapshot) => {
|
||||
var chatsArr = [];
|
||||
var messages = snapshot.val();
|
||||
for (var message in messages) {
|
||||
if (messages[message].isSystem) {
|
||||
chatsArr.push(
|
||||
<SystemMessage
|
||||
chatObj={messages[message]}
|
||||
key={messages[message].timestamp}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
chatsArr.push(
|
||||
<Chat
|
||||
chatObj={messages[message]}
|
||||
key={messages[message].timestamp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
var { register, control, reset, handleSubmit } = useForm();
|
||||
const [chats, setData] = useState(null);
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
|
||||
// Message updater
|
||||
useEffect(() => {
|
||||
onValue(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
|
||||
}/chats`
|
||||
),
|
||||
(snapshot) => {
|
||||
var chatsArr = [];
|
||||
var messages = snapshot.val();
|
||||
for (var message in messages) {
|
||||
if (messages[message].isSystem) {
|
||||
chatsArr.push(
|
||||
<SystemMessage
|
||||
chatObj={messages[message]}
|
||||
key={messages[message].timestamp}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
chatsArr.push(
|
||||
<Chat
|
||||
chatObj={messages[message]}
|
||||
key={messages[message].timestamp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
setData(chatsArr.reverse());
|
||||
setLoading(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function sendMessage(data) {
|
||||
reset();
|
||||
var payload = {
|
||||
body: data.message,
|
||||
user: user.username,
|
||||
uid: user.uid,
|
||||
isSystem: false,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
|
||||
}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) return <div>Loading</div>;
|
||||
if (!chats) return <div>No Chats</div>;
|
||||
return (
|
||||
<div className="m-1 h-[100%] rounded-lg">
|
||||
<div className="h-[90%] m-4 overflow-y-auto flex flex-col-reverse">
|
||||
{chats}
|
||||
</div>
|
||||
<div className="m-2 h-[10%] w-[100%] bg-white rounded-lg">
|
||||
<Form
|
||||
onSubmit={handleSubmit(sendMessage)}
|
||||
control={control}
|
||||
className="w-[100%] p-[0px]"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
{...register("message")}
|
||||
placeholder="Enter message"
|
||||
className="w-[83%] border-[0px] mt-[8px] mb-[8px]"
|
||||
/>
|
||||
<button className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5 w-[8%]">
|
||||
Send
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
setData(chatsArr.reverse());
|
||||
setLoading(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function sendMessage(data) {
|
||||
reset();
|
||||
var payload = {
|
||||
body: data.message,
|
||||
user: user.username,
|
||||
uid: user.uid,
|
||||
isSystem: false,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
|
||||
}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) return <div>Loading</div>;
|
||||
if (!chats) return <div>No Chats</div>;
|
||||
return (
|
||||
<div className="m-1 h-[100%] rounded-lg">
|
||||
<div className="h-[90%] m-4 overflow-y-auto flex flex-col-reverse">
|
||||
{chats}
|
||||
</div>
|
||||
<div className="m-2 h-[10%] w-[100%] bg-white rounded-lg">
|
||||
<Form
|
||||
onSubmit={handleSubmit(sendMessage)}
|
||||
control={control}
|
||||
className="w-[100%] p-[0px]"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
{...register("message")}
|
||||
placeholder="Enter message"
|
||||
className="w-[83%] border-[0px] mt-[8px] mb-[8px]"
|
||||
/>
|
||||
<button className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 w-[8%]">
|
||||
Send
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import {Geo} from "../datatypes"
|
||||
import { Geo } from "../map/geo";
|
||||
|
||||
// Module for Welcome Message on main tab landing page
|
||||
function WelcomeMessage({user}) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg m-2 mt-4 text-left p-2 pl-5">
|
||||
<div>
|
||||
Welcome, {user.firstName} {user.lastName} ({user.username})
|
||||
</div>
|
||||
<div>Lets see what's happening in your area.</div>
|
||||
function WelcomeMessage({ user }) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg m-2 mt-4 text-left p-2 pl-5">
|
||||
<div>
|
||||
Welcome, {user.firstName} {user.lastName} ({user.username})
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div>Lets see what's happening in your area.</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Primary App Landing Page
|
||||
export function MainTabHome({ loc, markers, user }) {
|
||||
return (
|
||||
<>
|
||||
<WelcomeMessage user={user}/>
|
||||
<div className="h-[calc(100%-110px)] m-5 rounded-lg">
|
||||
<Geo
|
||||
loc={loc}
|
||||
zoom={14}
|
||||
movable={true}
|
||||
locMarker={true}
|
||||
markers={markers}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<WelcomeMessage user={user} />
|
||||
<div className="h-[calc(100%-110px)] m-5 rounded-lg">
|
||||
<Geo
|
||||
loc={loc}
|
||||
zoom={14}
|
||||
movable={true}
|
||||
locMarker={true}
|
||||
markers={markers}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Map, Marker, ZoomControl } from "pigeon-maps";
|
||||
|
||||
// Map module for main page and chat room sidebar (and eventually user profile)
|
||||
// Constructs Map and Markers
|
||||
// TODO: Need to get rest of marker handling here or in marker file.
|
||||
export function Geo({ loc, zoom, locMarker, markers }) {
|
||||
if (loc) {
|
||||
return (
|
||||
<Map center={[loc.latitude, loc.longitude]} defaultZoom={zoom}>
|
||||
{markers && markers}
|
||||
{locMarker && (
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[loc.latitude, loc.longitude]}
|
||||
color="red"
|
||||
/>
|
||||
)}
|
||||
{zoom && <ZoomControl />}
|
||||
</Map>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Map className="rounded-lg" defaultCenter={[0, 0]} defaultZoom={zoom} />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Interests for Profile
|
||||
// Making this its own file since we could do a bit more with this in the future
|
||||
export function Interest({ interest }) {
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-lg m-2 p-2 shadow-xl">{interest}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { useForm, Form } from "react-hook-form";
|
||||
import { database, storage } from "../../../../firebase-config";
|
||||
import { ref as sRef, getDownloadURL } from "firebase/storage";
|
||||
import { ref, update } from "firebase/database";
|
||||
import { uploadBytes } from "firebase/storage";
|
||||
|
||||
export function ProfileEdit({ profileData, user, onSave }) {
|
||||
var { register, control } = useForm();
|
||||
|
||||
const handleEditState = () => {
|
||||
onSave(false);
|
||||
};
|
||||
|
||||
// Handles clicking save button
|
||||
function save({ data }) {
|
||||
|
||||
// Profile pic handling
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
handleEditState(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;
|
||||
handleEditState(false);
|
||||
update(ref(database, `users/${user.uid}`), data);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form onSubmit={save} encType={"application/json"} control={control}>
|
||||
<div className="grid grid-cols-2">
|
||||
<div>
|
||||
<img
|
||||
src={profileData.pfp}
|
||||
width="150px"
|
||||
className="relative mx-auto rounded-2xl overflow-hidden"
|
||||
/>
|
||||
Current Profile Picture
|
||||
</div>
|
||||
<div className="flex content-center">
|
||||
<input
|
||||
type="file"
|
||||
{...register("pfp")}
|
||||
className="w-[80%]"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 pl-2 w-[90%]">
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">First Name</div>
|
||||
<input
|
||||
className="w-[80%] border-2 border-gray-300 p-2 rounded-lg"
|
||||
type="text"
|
||||
{...register("firstName")}
|
||||
placeholder={profileData.firstName}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">Last Name</div>
|
||||
<input
|
||||
className="w-[80%] border-2 border-gray-300 p-2 rounded-lg"
|
||||
type="text"
|
||||
{...register("lastName")}
|
||||
placeholder={profileData.lastName}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">Username</div>
|
||||
<input
|
||||
className="w-[80%] border-2 border-gray-300 p-2 rounded-lg"
|
||||
type="text"
|
||||
{...register("username")}
|
||||
placeholder={profileData.username}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5">
|
||||
<div className="font-bold">Interests (Comma Seperated)</div>
|
||||
<input
|
||||
className="w-[80%] border-2 border-gray-300 p-2 rounded-lg"
|
||||
type="text"
|
||||
{...register("interests")}
|
||||
placeholder={profileData.interests}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5 col-span-2">
|
||||
<div className="font-bold">Bio</div>
|
||||
<textarea
|
||||
className="w-[92%] border-2 border-gray-300 p-2 rounded-lg"
|
||||
{...register("bio")}
|
||||
type="text"
|
||||
placeholder={profileData.bio}
|
||||
/>
|
||||
</div>
|
||||
<div className="justify-items-center pt-5 col-span-2">
|
||||
<button
|
||||
type="submit"
|
||||
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full text-center"
|
||||
>
|
||||
{" "}
|
||||
Save Changes{" "}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Geo } from "../map/geo";
|
||||
import { dateOptions } from "../datatypes";
|
||||
|
||||
// Display of Rooms on user profile
|
||||
export function ProfileRoom({ room }) {
|
||||
return (
|
||||
<div className="rounded-lg p-2 shadow-xl bg-white h-[250px] w-[325px]">
|
||||
<div className="relative z-1 h-[235px] opacity-50">
|
||||
<Geo
|
||||
loc={{ latitude: room.latitude, longitude: room.longitude }}
|
||||
zoom={12}
|
||||
locMarker={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative z-2 top-[-235px] text-left p-2">
|
||||
<div className="text-2xl font-bold">{room.name}</div>
|
||||
<div>{room.description}</div>
|
||||
<div>
|
||||
Created on {new Date(room.timestamp).toLocaleString(dateOptions)}
|
||||
</div>
|
||||
<a
|
||||
href={
|
||||
"/app?room=" + room.path + "/" + room.name + "-" + room.timestamp
|
||||
}
|
||||
className="absolute z-2 top-[190px] w-[108px] p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full flex items-center"
|
||||
>
|
||||
Open Room
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,38 +1,42 @@
|
||||
import { Geo } from "../datatypes";
|
||||
import { Geo } from "../map/geo";
|
||||
|
||||
export function Chat_Sidebar({chatRoomObj, chatroomOnline, chatroomUsersLoading, chatroomUsers, setTab}) {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className="m-2 h-[98%] grid grid-cols-1">
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl relative">
|
||||
<div className="w-[100%] h-[100%] opacity-50 absolute rounded-lg z-10">
|
||||
<Geo
|
||||
loc={{
|
||||
latitude: parseFloat(chatRoomObj.latitude.toFixed(2)),
|
||||
longitude: parseFloat(chatRoomObj.longitude.toFixed(2)),
|
||||
}}
|
||||
zoom={12}
|
||||
movable={false}
|
||||
marker={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="z-10 top-0 left-0 w-[100%] h-[100%] absolute text-left pl-3 pt-2">
|
||||
<span className="font-bold text-[24px]">
|
||||
{chatRoomObj.name}
|
||||
</span>
|
||||
<br />
|
||||
{chatRoomObj.description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl">
|
||||
<div>Online Members</div>
|
||||
{chatroomOnline}
|
||||
</div>
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl">
|
||||
<div>All Members</div>
|
||||
{!chatroomUsersLoading && chatroomUsers}
|
||||
</div>
|
||||
// Sidebar when in a Chatrooms
|
||||
export function Chat_Sidebar({
|
||||
chatRoomObj,
|
||||
chatroomOnline,
|
||||
chatroomUsersLoading,
|
||||
chatroomUsers,
|
||||
}) {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className="m-2 h-[98%] grid grid-cols-1">
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl relative">
|
||||
<div className="w-[100%] h-[100%] opacity-50 absolute rounded-lg z-10">
|
||||
<Geo
|
||||
loc={{
|
||||
latitude: parseFloat(chatRoomObj.latitude.toFixed(2)),
|
||||
longitude: parseFloat(chatRoomObj.longitude.toFixed(2)),
|
||||
}}
|
||||
zoom={12}
|
||||
movable={false}
|
||||
marker={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="z-10 top-0 left-0 w-[100%] h-[100%] absolute text-left pl-3 pt-2">
|
||||
<span className="font-bold text-[24px]">{chatRoomObj.name}</span>
|
||||
<br />
|
||||
{chatRoomObj.description}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl">
|
||||
<div>Online Members</div>
|
||||
{chatroomOnline}
|
||||
</div>
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl">
|
||||
<div>All Members</div>
|
||||
{!chatroomUsersLoading && chatroomUsers}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,125 +2,132 @@ import { Form, useForm } from "react-hook-form";
|
||||
import { database } from "../../../../firebase-config";
|
||||
import { ref, set } from "firebase/database";
|
||||
|
||||
// Sidebar on Home Page, with various functionality (create, nearby, my rooms)
|
||||
|
||||
// CreateRoom Module for Sidebar Create Tab
|
||||
function CreateRoom({ loc }) {
|
||||
var { register, control, reset, handleSubmit } = useForm();
|
||||
var { register, control, reset, handleSubmit } = useForm();
|
||||
|
||||
function createRoom(data) {
|
||||
reset();
|
||||
var path =
|
||||
String(loc.latitude.toFixed(2)).replace(".", "") +
|
||||
"/" +
|
||||
String(loc.longitude.toFixed(2)).replace(".", "");
|
||||
var timestamp = new Date().getTime();
|
||||
var payload = {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
timestamp: timestamp,
|
||||
latitude: loc.latitude,
|
||||
longitude: loc.longitude,
|
||||
path: path,
|
||||
};
|
||||
set(ref(database, `/rooms/${path}/${data.name}-${timestamp}`), payload);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<Form control={control} onSubmit={handleSubmit(createRoom)}>
|
||||
<input
|
||||
{...register("name")}
|
||||
placeholder="Room Name"
|
||||
className="mt-2"
|
||||
/>
|
||||
<input
|
||||
{...register("description")}
|
||||
placeholder="Room Description"
|
||||
className="mt-2"
|
||||
/>
|
||||
<br />
|
||||
<div className="mt-3 mb-2">
|
||||
Creating room near ({loc.latitude.toFixed(2)},{" "}
|
||||
{loc.longitude.toFixed(2)})
|
||||
</div>
|
||||
<button className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5">
|
||||
Create
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
function createRoom(data) {
|
||||
reset();
|
||||
var path =
|
||||
String(loc.latitude.toFixed(2)).replace(".", "") +
|
||||
"/" +
|
||||
String(loc.longitude.toFixed(2)).replace(".", "");
|
||||
var timestamp = new Date().getTime();
|
||||
var payload = {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
timestamp: timestamp,
|
||||
latitude: loc.latitude,
|
||||
longitude: loc.longitude,
|
||||
path: path,
|
||||
};
|
||||
set(ref(database, `/rooms/${path}/${data.name}-${timestamp}`), payload);
|
||||
}
|
||||
|
||||
export function Home_Sidebar({tab, nearby, loadingNearby, setTab, isRoomLoading, myRooms, loadingLoc, location}) {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className="bg-white shadow-2xl rounded-lg m-2 h-[98%]">
|
||||
<div className="p-2">
|
||||
<div className="p-1 rounded-lg grid grid-cols-3 bg-white">
|
||||
<div
|
||||
className={
|
||||
tab == "nearby"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("nearby");
|
||||
}}
|
||||
>
|
||||
Nearby
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
tab == "rooms"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("rooms");
|
||||
}}
|
||||
>
|
||||
My Rooms
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
tab == "create"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("create");
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<Form control={control} onSubmit={handleSubmit(createRoom)}>
|
||||
<input {...register("name")} placeholder="Room Name" className="mt-2" />
|
||||
<input
|
||||
{...register("description")}
|
||||
placeholder="Room Description"
|
||||
className="mt-2"
|
||||
/>
|
||||
<br />
|
||||
<div className="mt-3 mb-2">
|
||||
Creating room near ({loc.latitude.toFixed(2)},{" "}
|
||||
{loc.longitude.toFixed(2)})
|
||||
</div>
|
||||
<button className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5">
|
||||
Create
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Home_Sidebar({
|
||||
tab,
|
||||
nearby,
|
||||
loadingNearby,
|
||||
setTab,
|
||||
isRoomLoading,
|
||||
myRooms,
|
||||
loadingLoc,
|
||||
location,
|
||||
}) {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className="bg-white shadow-2xl rounded-lg m-2 h-[98%]">
|
||||
<div className="p-2">
|
||||
<div className="p-1 rounded-lg grid grid-cols-3 bg-white">
|
||||
<div
|
||||
className={
|
||||
tab == "nearby"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("nearby");
|
||||
}}
|
||||
>
|
||||
Nearby
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
tab == "rooms"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("rooms");
|
||||
}}
|
||||
>
|
||||
My Rooms
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
tab == "create"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("create");
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</div>
|
||||
{tab == "nearby" && (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<div>
|
||||
{!nearby && !loadingNearby && (
|
||||
<div>
|
||||
No Nearby Rooms
|
||||
<br />
|
||||
Create One?
|
||||
</div>
|
||||
)}
|
||||
{loadingNearby && <div>Loading...</div>}
|
||||
{nearby}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tab == "rooms" && (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<div>
|
||||
{isRoomLoading && <div>Loading</div>}
|
||||
{!myRooms && !isRoomLoading && <div>No User Saved Rooms</div>}
|
||||
{myRooms}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tab == "create" && !loadingLoc && <CreateRoom loc={location} />}
|
||||
{tab == "create" && loadingLoc && <div>Loading...</div>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{tab == "nearby" && (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<div>
|
||||
{!nearby && !loadingNearby && (
|
||||
<div>
|
||||
No Nearby Rooms
|
||||
<br />
|
||||
Create One?
|
||||
</div>
|
||||
)}
|
||||
{loadingNearby && <div>Loading...</div>}
|
||||
{nearby}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tab == "rooms" && (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<div>
|
||||
{isRoomLoading && <div>Loading</div>}
|
||||
{!myRooms && !isRoomLoading && <div>No User Saved Rooms</div>}
|
||||
{myRooms}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tab == "create" && !loadingLoc && <CreateRoom loc={location} />}
|
||||
{tab == "create" && loadingLoc && <div>Loading...</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export function Profile_Sidebar() {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className=" bg-white m-2 h-[98%]">Profile</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className=" bg-white m-2 h-[98%]">Profile</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Generated
-102
@@ -1,102 +0,0 @@
|
||||
{
|
||||
"name": "ChatMaps",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"react-router-dom": "^6.22.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
|
||||
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
|
||||
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
|
||||
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.3",
|
||||
"react-router": "6.22.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"react-router-dom": "^6.22.3"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user