Various Bug Fixes and Additions (#102)

This commit was merged in pull request #102.
This commit is contained in:
Nicholas Pease
2024-04-23 19:48:43 -04:00
committed by GitHub
22 changed files with 3439 additions and 1677 deletions
+2 -1
View File
@@ -4,6 +4,7 @@
"no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }],
"jsx-a11y/alt-text": "off",
"@next/next/no-img-element": "off",
"no-console": 1
"no-console": 1,
"react-hooks/exhaustive-deps": "off"
}
}
+3289 -1524
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -10,8 +10,8 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
+4 -3
View File
@@ -10,8 +10,9 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
+1 -1
View File
@@ -5,7 +5,7 @@ import { useState, useEffect } from "react";
// Firebase Imports
import { auth, database } from "../../../firebase-config";
import { ref, onValue, set, onDisconnect, get, onChildAdded, onChildRemoved } from "firebase/database";
import { ref, onValue, set, onDisconnect} from "firebase/database";
import { useAuthState } from "react-firebase-hooks/auth"
// Component Imports
+4 -3
View File
@@ -10,8 +10,9 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
-1
View File
@@ -15,7 +15,6 @@ button {
border-radius: 5px;
padding: 5px;
margin: 5px;
filter: drop-shadow(0 25px 25px rgb(0 0 0 / 0.15));
}
button:hover {
+3 -3
View File
@@ -10,8 +10,8 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
+4 -4
View File
@@ -31,7 +31,7 @@ function Login() {
}
}
).catch((error) => {
if (error = "auth/invalid-credential") {
if (error == "auth/invalid-credential") {
const formError = {
type: "server",
message: "Username or Password Incorrect",
@@ -99,20 +99,20 @@ function Login() {
{(isSubmitting || isSubmitted) && (
<span className="inline-block">
<svg
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
className="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"
className="opacity-25"
cx="12"
cy="12"
r="10"
strokeWidth="4"
></circle>
<path
class="opacity-75"
className="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>
+4 -3
View File
@@ -10,8 +10,9 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
+3 -3
View File
@@ -46,9 +46,9 @@ function Home() {
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
if (snapshot.exists()) {
var count = 0;
for (var room in snapshot.val()) {
count += 1;
}
snapshot.forEach(() => {
count++;
});
setRoomCount(count);
} else {
setRoomCount(0);
+3 -3
View File
@@ -10,8 +10,8 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
+3 -3
View File
@@ -10,8 +10,8 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
<main className={inter.className}>
{children}
</main>
);
}
+6 -6
View File
@@ -2,7 +2,7 @@
// System Imports
import { useState, useEffect } from "react";
import { auth, database } from "../../../firebase-config";
import { ref, onValue, get } from "firebase/database";
import { ref, onValue } from "firebase/database";
import { useAuthState } from "react-firebase-hooks/auth"
@@ -49,7 +49,7 @@ function UserProfile() {
if (authUser && authLoading === false) {
const searchParams = new URLSearchParams(document.location.search);
var userUID = searchParams.get("uid")
get(ref(database, `users/${authUser.uid}`)).then((userData) => {
onValue(ref(database, `users/${authUser.uid}`), (userData) => {
userData = userData.val();
if (userData) {
if (userData.uid == userUID) {
@@ -70,7 +70,7 @@ function UserProfile() {
useEffect(() => {
const searchParams = new URLSearchParams(document.location.search);
var userUID = searchParams.get("uid")
get(ref(database, "/users/" + userUID)).then((snapshot) => {
onValue(ref(database, "/users/" + userUID), (snapshot) => {
setProfileData(snapshot.val());
// Populates array with user's interests
@@ -84,7 +84,7 @@ function UserProfile() {
var i = 0;
for (var interest in interests) {
if (i < 4)
interestArray.push(<Interest interest={interests[interest]} />);
interestArray.push(<Interest interest={interests[interest]} key={interest}/>);
i++;
}
setUserInterestArray(interestArray);
@@ -93,7 +93,7 @@ function UserProfile() {
var rooms = snapshot.val().rooms;
var roomArray = [];
for (var room in rooms) {
roomArray.push(<ProfileRoom room={rooms[room]} />);
roomArray.push(<ProfileRoom room={rooms[room]} key={room}/>);
}
setUserRoomsArray(roomArray);
});
@@ -119,7 +119,7 @@ function UserProfile() {
{/* Left Side of Page */}
<div className="h-dvh md:overflow-hidden">
{/* Header */}
<Header user={user} />
<Header user={user} mainTab={"profile"} />
{/* Main Page Section */}
<div className="md:grid md:grid-cols-3 mr-2 h-[calc(100%-110px)] pl-5 pr-5 pt-2 max-md:mb-10">
<div className="cols-span-1 bg-white shadow-2xl rounded-xl pt-5 max-md:pb-5">
+86 -52
View File
@@ -11,7 +11,16 @@ import PersonIcon from '@mui/icons-material/Person';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import CircleIcon from '@mui/icons-material/Circle';
import { useTts, TextToSpeech } from 'tts-react'
import { TextToSpeech } from 'tts-react'
// Chat Commands Dictionary
const chatCommands = {
"/peter": "https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExcThtYjMxcWk2NG55YTE3OHdvNWJwcXVrZzV1ZmZkdWJ6cHBremFwdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/13w5HmyiuaZ224/giphy.gif",
"/shrug": "¯\\_(ツ)_/¯",
"/tableflip": "(╯°□°)╯︵ ┻━┻",
"/unflip": "┬─┬ ( ゜-゜ノ)",
"/snoop": "https://media1.tenor.com/m/cIUjlvgnFRgAAAAd/dog-snoop.gif"
}
// Colors for Messages
const userColors = [
@@ -45,18 +54,17 @@ let dateOptions = {
* @returns {Boolean} - Image Loaded (True) or Not (False)
*/
function imageProcessing(url) {
var x = async () => {
var x = new Promise((resolve) => {
var img = new Image();
img.src = url;
img.onload = function() {
return true;
img.onload = () => {
resolve([true, url]);
}
img.onerror = function() {
return false;
img.onerror = () => {
resolve([false, url]);
}
}
var res = x()
return res
})
return x
}
@@ -65,28 +73,42 @@ function imageProcessing(url) {
* @param {String} message - Message to Format
* @returns {String} - Formatted Message (IN HTML)
*/
export function RMF(message) {
var URLREGEX = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
var URLmatch = message.match(URLREGEX);
var newMessage = URLmatch ? [] : message
if (URLmatch) {
for (var i = 0; i < URLmatch.length; i++) {
if (imageProcessing("https://"+URLmatch[i])) {
newMessage.push((<span className="mr-2">
{(URLmatch.length == 1) && message.split(URLmatch[i])[0].replace("https://","").replace("http://","")}
<img src={"https://"+URLmatch[i]} className="max-w-[100%]"/>
{(i == URLmatch.length || URLmatch.length == 1) && message.split(URLmatch[i])[1]}
</span>))
} else {
newMessage.push((<span className="mr-2">
{URLmatch.length == 1 && message.split(URLmatch[i])[0]}
<Link href={"https://"+URLmatch[i]} target="_blank" className="hover:underline">{URLmatch[i]}</Link>
{(i == URLmatch.length || URLmatch.length == 1) && message.split(URLmatch[i])[1]}
</span>))
export async function RMF(message) {
var x = new Promise(async (resolve) => {
var URLREGEX = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
var URLmatch = message.match(URLREGEX);
var newMessage = URLmatch ? [] : message
if (URLmatch) {
for (var i = 0; i < URLmatch.length; i++) {
if (URLmatch[i].includes("chatma.ps") ) {
// Rich Message Formatting for Chat Maps
if (URLmatch[i].includes("/chat?")) {
var roomName = URLmatch[i].split("?")[1].split("/")[2].split("-")[0].replaceAll("%20"," ")
newMessage.push((<span className="italic" key={roomName}>invites you to <Link href={"https://"+URLmatch[i]} className="underline">{roomName}</Link></span>))
}
} else {
await imageProcessing("https://"+URLmatch[i]).then((result) => {
if (result[0]) {
newMessage.push((<span className="mr-2" key={URLmatch[i]}>
{(URLmatch.length == 1) && message.split(result[1])[0].replace("https://","").replace("http://","")}
<img src={result[1]} className="max-w-[100%]"/>
{(i == URLmatch.length || URLmatch.length == 1) && message.split(result[1])[1]}
</span>))
} else {
newMessage.push((<span className="mr-2" key={URLmatch[i]}>
{URLmatch.length == 1 && message.split(URLmatch[i])[0]}
<Link href={result[1]} target="_blank" className="hover:underline">{URLmatch[i]}</Link>
{(i == URLmatch.length || URLmatch.length == 1) && message.split(URLmatch[i])[1]}
</span>))
}
})
}
}
}
}
return newMessage
resolve(newMessage)
});
return x
}
/**
* Grabs Window Size
@@ -134,35 +156,47 @@ const generateColor = (user_str) => {
* @returns {Object} - Chat Message Component
*/
export function Chat({ chatObj, user, path }) {
const [message, setMessage] = useState([])
function deleteMessage() {
remove(ref(database, `${path}/chats/${chatObj.timestamp}-${chatObj.user}`))
}
var messageFilterBypass = [undefined, null, '', ' ', '\'', '\"']
if (!messageFilterBypass.includes(chatObj.body) && (chatObj.body.length != 1 && !chatObj.body[0].match(/\W/))) {
var message = filter.clean(chatObj.body)
message = RMF(message)
} else {
var message = chatObj.body
if (chatObj.body in chatCommands) {
chatObj.body = chatCommands[chatObj.body]
}
const Speak = ({ children }) => (
<>{useTts({ children, autoPlay: true }).ttsChildren}</>
)
useEffect(() => {
// Peter Griffin Easter Egg and others
var messageFilterBypass = [undefined, null, '', ' ', '\'', '\"']
if (!messageFilterBypass.includes(chatObj.body) && (chatObj.body.length != 1 && !chatObj.body[0].match(/\W/))) {
var settingMessage = filter.clean(chatObj.body)
RMF(settingMessage).then((result) => {
setMessage(result)
})
} else {
setMessage(chatObj.body)
}
}, [])
return (
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
<div>
{user.uid == chatObj.uid && <DeleteOutlineIcon fontSize="" className="ml-1 mr-1 cursor-pointer" onClick={() => {deleteMessage()}}/>}
<span className="mr-[5px]" style={{ color: userColors[generateColor(chatObj.user)] }}>
<Link href={`/user?uid=${chatObj.uid}`}
className="hover:font-bold cursor-pointer">
{chatObj.user}
</Link>
</span>
{(typeof message == 'string' && message.substring(0,4) == "/tts")? (<TextToSpeech autoPlay={true}> {chatObj.user + " said " + message.substring(5,message.length)} </TextToSpeech>): message}
</div>
<div className="text-right text-[#d1d1d1]">
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
</div>
<div>
{message && (
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
<div>
{user.uid == chatObj.uid && <DeleteOutlineIcon fontSize="" className="ml-1 mr-1 cursor-pointer" onClick={() => {deleteMessage()}}/>}
<span className="mr-[5px]" style={{ color: userColors[generateColor(chatObj.user)] }}>
<Link href={`/user?uid=${chatObj.uid}`}
className="hover:font-bold cursor-pointer">
{chatObj.user}
</Link>
</span>
{(typeof message == 'string' && message.substring(0,4) == "/tts")? (<TextToSpeech autoPlay={true}> {chatObj.user + " said " + filter.clean(message.substring(5,message.length))} </TextToSpeech>): message}
</div>
<div className="text-right text-[#d1d1d1]">
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
</div>
</div>
)}
</div>
);
}
@@ -71,7 +71,7 @@ export function DMRoom({ roomObj, user }) {
chatObj={messages[message]}
user={user}
path={"/dms/" + chatRoomObj.room}
key={messages[message].timestamp}
key={messages[message].timestamp + "-" + messages[message].user}
/>
);
}
+11 -13
View File
@@ -14,6 +14,7 @@ import MenuIcon from '@mui/icons-material/Menu';
import AddIcon from '@mui/icons-material/Add';
import RemoveIcon from '@mui/icons-material/Remove';
import CloseIcon from '@mui/icons-material/Close';
import { useEffect } from "react";
/**
* Closes Chat
@@ -95,12 +96,14 @@ export function Header({mainTab,chatRoomObj,user,sidebarControl}) {
}
// Sets User Online / Offline
// Stored in header for easy code maintenance and retains user online/offline throughout app
// Makes user online
if (user.invisibleStatus == false) {
set(ref(database, `/users/${user.uid}/lastOnline`), true)
}
useEffect(() => {
// Sets User Online / Offline
// Stored in header for easy code maintenance and retains user online/offline throughout app
// Makes user online
if (user.invisibleStatus == false) {
set(ref(database, `/users/${user.uid}/lastOnline`), true)
}
}, [])
// Makes user offline (with last time online)
onDisconnect(ref(database, `/users/${user.uid}/lastOnline`)).set(serverTimestamp())
@@ -152,13 +155,8 @@ export function Header({mainTab,chatRoomObj,user,sidebarControl}) {
<ProfilePanel user={user}/>
{/* Sidebar Control (for small screens) */}
<div
className="md:hidden p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
onClick={() => {sidebarControl()}}
>
<MenuIcon/>
</div>
</div>
{mainTab !== "profile" && (<div className="md:hidden p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center" onClick={() => {sidebarControl()}}><MenuIcon/></div>)}
</div>
</div>
);
}
@@ -6,44 +6,6 @@ import ChatBubbleTwoToneIcon from '@mui/icons-material/ChatBubbleTwoTone';
import PersonOutlineTwoToneIcon from '@mui/icons-material/PersonOutlineTwoTone';
import { red } from '@mui/material/colors';
/**
* Nearby Markers Grabber
* @param {JSON} location - Location Object {latitude, longitude}
* @returns {Array} - Array of Markers {<Marker>}
*/
function NearbyMarkers(location) {
const [newMarkers, setNewMarkers] = useState(null);
if (location) {
const path = String(location.latitude.toFixed(2)).replace(".", "") +"/" +String(location.longitude.toFixed(2)).replace(".", "") +"/";
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
if (snapshot.exists()) {
const rooms = snapshot.val();
setNewMarkers(rooms)
}
})
}
return newMarkers;
}
/**
* Friend Markers Grabber
* @param {JSON} user - User Object
* @returns {Array} - Array of Markers {<Marker>}
*/
function FriendMarkers(user) {
var friendMarkers = []
if (user && "friends" in user && "friends" in user.friends) {
for (var friend in user.friends.friends) {
get(ref(database, `/users/${friend}`)).then((snapshot) => {
var friendData = snapshot.val();
if (friendData.location) {
friendMarkers.push(friendData);
}
});
}
}
}
/**
* Geo Component for Wrapping Map
* @constructor
@@ -56,7 +56,7 @@ export function ChatRoom({ roomObj, user }) {
chatsArr.push(
<SystemMessage
chatObj={messages[message]}
key={messages[message].timestamp}
key={messages[message].timestamp + messages[message].user}
/>
);
} else {
@@ -65,7 +65,7 @@ export function ChatRoom({ roomObj, user }) {
chatObj={messages[message]}
user={user}
path={"/rooms/" + chatRoomObj.path + "/" + chatRoomObj.name + "-" + chatRoomObj.timestamp}
key={messages[message].timestamp}
key={messages[message].timestamp + "-" + messages[message].user}
/>
);
}
@@ -17,7 +17,7 @@ export function Sidebar({chatRoomObj}) {
var activeUsers = [];
var activeUsersJSON = chatRoomObj.users.online;
for (var user in activeUsersJSON)
activeUsers.push(<Member memberObj={activeUsersJSON[user]} />);
activeUsers.push(<Member memberObj={activeUsersJSON[user]} key={user}/>);
var chatroomOnline = activeUsers
}
@@ -29,7 +29,7 @@ export function Sidebar({chatRoomObj}) {
var allUsers = [];
var allUsersJSON = chatRoomObj.users.all;
for (var user in allUsersJSON)
allUsers.push(<Member memberObj={allUsersJSON[user]} />);
allUsers.push(<Member memberObj={allUsersJSON[user]} key={user}/>);
var chatroomUsers = allUsers
}
return (
@@ -168,42 +168,42 @@ export function Sidebar({user,location,loadingLoc}) {
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold bg-[#D3D3D3] drop-shadow-none'
)}>Nearby</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold bg-[#D3D3D3] drop-shadow-none'
)}>My Rooms</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold bg-[#D3D3D3] drop-shadow-none'
)}>Create</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold bg-[#D3D3D3] drop-shadow-none'
)}>DMs</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold bg-[#D3D3D3] drop-shadow-none'
)}>Friends</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold bg-[#D3D3D3] drop-shadow-none'
)}>Requests</Tab>
</Tab.List>
@@ -10,7 +10,7 @@ export function NearbySidebar({location}) {
const [nearbyArr, setNearbyArr] = useState([])
const [displayedRooms, setDisplayedRooms] = useState([])
const [nearbyArrReady, setNearbyArrReady] = useState(false)
const {register, watch, setFocus} = useForm({defaultValues: {search: null}})
const {register, watch, setFocus} = useForm({defaultValues: {search: ""}})
// Search Bar Value
const search = watch("search")
@@ -19,7 +19,7 @@ export function NearbySidebar({location}) {
function SearchBar() {
return (
<div className="w-[97%]">
<input type="text" placeholder="Search" {...register("search")} className="w-full p-2 border-2 border-gray-300 rounded-lg col-span-3" value={null} />
<input type="text" placeholder="Search" {...register("search")} className="w-full p-2 border-2 border-gray-300 rounded-lg col-span-3" value={" "} />
</div>
)
}