Update Chat/App/Profile Pages to be Mobile Friendly #61

Merged
LAX18 merged 6 commits from npease-mobile-friendly into main 2024-04-05 17:33:08 -09:00
11 changed files with 143 additions and 60 deletions
+29 -8
View File
@@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
// Dependencies
import { useGeolocated } from "react-geolocated";
import Drawer from '@mui/material/Drawer';
// Firebase Imports
import { auth, database } from "../../../firebase-config";
@@ -14,6 +15,7 @@ import { useAuthState } from "react-firebase-hooks/auth"
import { Header } from "../../components/app/header";
import { HomePage } from "../../components/app/page/home";
import { Sidebar } from "../../components/app/sidebar/home";
import {useWindowSize} from "../../components/app/datatypes";
/**
* Contains most everything for the app homepage
@@ -24,6 +26,16 @@ function Home() {
const [user, setUser] = useState(null); // user data
const [loadingLoc, setLoadingLoc] = useState(true); // location variable loading, true = loading, false = finished loading
const [authUser] = useAuthState(auth) // auth user object (used to obtain other user object)
const [drawerOpen, setDrawerOpen] = useState(true); // drawer open state
var windowSize = useWindowSize()
useEffect(() => {
if (windowSize.width < 767) {
setDrawerOpen(false)
} else {
setDrawerOpen(true)
}
}, [windowSize])
// Authentication Verification / Redirection if Profile Data not Filled out
useEffect(() => {
@@ -55,15 +67,16 @@ function Home() {
}, [coords])
return (
<div>
<div className="overflow-hidden h-dvh">
{user && (
<div className="grid grid-cols-4 auto-cols-max overflow-hidden">
<div className="overflow-hidden h-dvh">
{/* Left Side of Page */}
<div className="col-span-3 h-dvh">
<div className="overflow-hidden h-dvh md:mr-[405px]">
{/* Header */}
<Header
mainTab={"home"}
user={user}
sidebarControl={() => {setDrawerOpen(!drawerOpen)}}
/>
{/* Main Page Section */}
<div className="mr-2 h-[calc(100%-110px)]">
@@ -76,11 +89,19 @@ function Home() {
</div>
</div>
{/* Sidebar (Right Side of Page) */}
<Sidebar
user={user}
location={coords}
loadingLoc={loadingLoc}
/>
<Drawer open={drawerOpen} anchor={"right"} variant={windowSize.width > 767? "persistent": "temporary"} onClose={() => {setDrawerOpen(false)}} sx={{
width: windowSize.width > 767? 400: "80%",
marginTop: 10,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: windowSize.width > 767? 400: "80%",
borderLeft: 0,
},
}}>
<div className="shadow-2xl">
<Sidebar user={user} location={coords} loadingLoc={loadingLoc}/>
</div>
</Drawer>
</div>
)}
</div>
+30 -6
View File
@@ -1,16 +1,19 @@
"use client";
// System Imports
import Drawer from '@mui/material/Drawer';
import { useState, useEffect } from "react";
// Firebase Imports
import { auth, database } from "../../../firebase-config";
import { ref, onValue, set, onDisconnect, serverTimestamp } from "firebase/database";
import { ref, onValue, set, onDisconnect } from "firebase/database";
import { useAuthState } from "react-firebase-hooks/auth"
// Component Imports
import { Header } from "../../components/app/header";
import { ChatRoom } from "../../components/app/page/chat";
import { Sidebar } from "../../components/app/sidebar/chat";
import {useWindowSize} from "../../components/app/datatypes";
/**
* Chat Page
@@ -22,6 +25,16 @@ function Chat() {
const [chatRoomObj, setChatRoomObj] = useState(null); // Current chatroom object
const [doneLoading, setDoneLoading] = useState(false) // is the page done loading or not
const [authUser] = useAuthState(auth) // auth user object (used to obtain other user object)
const [drawerOpen, setDrawerOpen] = useState(true); // drawer open state
var windowSize = useWindowSize()
useEffect(() => {
if (windowSize.width < 767) {
setDrawerOpen(false)
} else {
setDrawerOpen(true)
}
}, [windowSize])
// Authentication Verification / Redirection if Profile Data not Filled out
useEffect(() => {
@@ -88,14 +101,15 @@ function Chat() {
return (
<div>
{(authUser && doneLoading) && (
<div className="grid grid-cols-4 auto-cols-max overflow-hidden">
<div className="overflow-hidden h-dvh">
{/* Left Side of Page */}
<div className="col-span-3 h-dvh">
<div className="overflow-hidden h-dvh md:mr-[400px]">
{/* Header */}
<Header
mainTab={"chat"}
chatRoomObj={chatRoomObj}
user={user}
sidebarControl={() => {setDrawerOpen(!drawerOpen)}}
/>
{/* Main Page Section */}
<div className="mr-2 h-[calc(100%-110px)]">
@@ -103,9 +117,19 @@ function Chat() {
</div>
</div>
{/* Sidebar (Right Side of Page) */}
<Sidebar
chatRoomObj={chatRoomObj}
/>
<Drawer open={drawerOpen} anchor={"right"} variant={windowSize.width > 767? "persistent": "temporary"} onClose={() => {setDrawerOpen(false)}} sx={{
width: windowSize.width > 767? 400: "80%",
marginTop: 10,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: windowSize.width > 767? 400: "80%",
borderLeft: 0,
},
}}>
<div className="shadow-2xl">
<Sidebar chatRoomObj={chatRoomObj}/>
</div>
</Drawer>
</div>
)}
</div>
@@ -1,5 +1,5 @@
import { Inter } from "next/font/google";
import "../../globals.css";
import "../globals.css";
const inter = Inter({ subsets: ["latin"] });
@@ -1,26 +1,25 @@
"use client";
// System Imports
import { useState, useEffect } from "react";
import { auth, database } from "../../../../firebase-config";
import { auth, database } from "../../../firebase-config";
import { ref, onValue, get } from "firebase/database";
import { onAuthStateChanged } from "firebase/auth";
// Refactored Component Imports
// Data Structure Imports
import { ProfileRoom } from "../../../components/app/profile/ProfileRoom";
import { ProfileEdit } from "../../../components/app/profile/ProfileEdit";
import { Interest } from "../../../components/app/profile/Interest";
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 { Header } from "../../components/app/header";
/**
* User Profile Page
* @param {URLSearchParams} params - URL Parameters
* @returns {Object} - User Profile Page
*/
function UserProfile({ params }) {
function UserProfile() {
const [profileData, setProfileData] = useState(null); // Profile Data
const [isAuthenticated, setIsAuthenticated] = useState(false); // Determines if user is authenticated
const [user, setUser] = useState(null); // User Data
@@ -36,12 +35,15 @@ function UserProfile({ params }) {
// Authentication
useEffect(() => {
onAuthStateChanged(auth, (user) => {
const searchParams = new URLSearchParams(document.location.search);
var userUID = searchParams.get("uid")
if (user) {
get(ref(database, `users/${user.uid}`)).then((userData) => {
userData = userData.val();
if (userData) {
if (userData.uid == params.stub) {
if (userData.uid == userUID) {
setIsOwner(true);
}
setUser(userData);
@@ -59,7 +61,9 @@ function UserProfile({ params }) {
// Grabs profile user data
useEffect(() => {
onValue(ref(database, "/users/" + params.stub), (snapshot) => {
const searchParams = new URLSearchParams(document.location.search);
var userUID = searchParams.get("uid")
onValue(ref(database, "/users/" + userUID), (snapshot) => {
setProfileData(snapshot.val());
// Populates array with user's interests
@@ -91,14 +95,14 @@ function UserProfile({ params }) {
return (
<div>
{isAuthenticated && (
<div className="overflow-hidden">
<div className="md:overflow-hidden">
{/* Left Side of Page */}
<div className="h-dvh overflow-hidden">
<div className="h-dvh md:overflow-hidden">
{/* Header */}
<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">
<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">
{!isEditing && (
<div>
<img
@@ -142,7 +146,7 @@ function UserProfile({ params }) {
)}
</div>
<div className="col-span-2">
<div className="grid grid-cols-3 gap-y-1 pl-5 gap-1 h-[100%] w-[100%]">
<div className="grid md:grid-cols-3 max-md:grid-cols-1 max-md:mt-5 md:pl-5 justify-items-center gap-y-5 gap-1 h-[100%] w-[100%]">
{userRoomsArray}
</div>
</div>
+29 -3
View File
@@ -1,4 +1,5 @@
import Link from "next/link"
import { useEffect, useState } from "react";
// Colors for Messages
const userColors = [
@@ -26,6 +27,31 @@ let dateOptions = {
minute: "2-digit",
};
/**
* Grabs Window Size
* @returns {Object} - Window Size Object (width, height)
*/
export function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener("resize", handleResize);
handleResize();
return () => window.removeEventListener("resize", handleResize);
}, []);
return windowSize;
}
/**
* Generates Color based on string hash
* @param {String} user_str - Username / String for hashing
@@ -52,7 +78,7 @@ export function Chat({ chatObj }) {
<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)] }}>
<Link href={`/user/${chatObj.uid}`}
<Link href={`/user?uid=${chatObj.uid}`}
className="hover:font-bold cursor-pointer"
target="_blank">
{chatObj.user}
@@ -78,7 +104,7 @@ export function SystemMessage({ chatObj }) {
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
<div className="text-[#d1d1d1]">
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
<Link href={`/user/${chatObj.uid}`}
<Link href={`/user?uid=${chatObj.uid}`}
className="hover:font-bold cursor-pointer"
target="_blank">
{chatObj.user}
@@ -100,7 +126,7 @@ export function SystemMessage({ chatObj }) {
*/
export function Member({ memberObj }) {
return (
<Link href={"/user/" + memberObj.uid} target="_blank">
<Link href={"/user?uid=" + memberObj.uid} target="_blank">
<div className="cursor-pointer g-[aliceblue] rounded-lg m-3 shadow-xl p-2">
{memberObj.username}
</div>
+23 -8
View File
@@ -9,6 +9,12 @@ import { ref, set, remove } from "firebase/database";
import { NotificationPanel } from "./notifications/notifications";
import { ProfilePanel } from "./profile/ProfilePanel"
// Icons
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';
/**
* Closes Chat
* @param {JSON} chatRoomObj - Chat Room Object
@@ -69,7 +75,7 @@ function removeFromMyRooms(chatRoomObj, user) {
* @prop {JSON} chatRoomObj - Chat Room Object
* @prop {JSON} user - User Object
*/
export function Header({mainTab,chatRoomObj,user,}) {
export function Header({mainTab,chatRoomObj,user,sidebarControl}) {
if (mainTab == "chat") {
var roomName = chatRoomObj.name + "-" + chatRoomObj.timestamp;
@@ -86,7 +92,8 @@ export function Header({mainTab,chatRoomObj,user,}) {
<div className="flex m-2 rounded-lg h-[63px] bg-white shadow-2xl p-1">
<div className="flex shrink h-[60px]">
<Link href="/app">
<img src="/logos/logo_transparent_inverse.png" className="h-[60px]" />
<img src="/logos/logo_transparent_inverse.png" className="h-[60px] max-xl:hidden" />
<img src="/logos/icon.png" className="h-[50px] mt-[5px] mb-[5px] xl:hidden" />
</Link>
</div>
<div className="grow grid grid-rows-1 grid-flow-col auto-cols-max justify-end gap-2 h-[60px] p-2">
@@ -96,9 +103,9 @@ export function Header({mainTab,chatRoomObj,user,}) {
addToMyRooms(chatRoomObj, user);
}}
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-2 flex items-center"
>
Add to &quot;My Rooms&quot;
<AddIcon/>
</a>
)}
{mainTab == "chat" && isMyRoom == true && (
@@ -107,18 +114,18 @@ export function Header({mainTab,chatRoomObj,user,}) {
removeFromMyRooms(chatRoomObj, user);
}}
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-2 flex items-center"
>
Remove from &quot;My Rooms&quot;
<RemoveIcon/>
</a>
)}
{mainTab == "chat" && (
<Link
href="/app"
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 flex items-center"
className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-2 flex items-center"
onClick={() => {closeChat(chatRoomObj,user)}}
>
Close Chat
<CloseIcon/>
</Link>
)}
@@ -127,6 +134,14 @@ export function Header({mainTab,chatRoomObj,user,}) {
{/*Profile Dropdown */}
<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>
</div>
);
@@ -86,7 +86,7 @@ export function NotificationPanel({user}) {
</div>
</Popover.Button>
<Popover.Panel className="absolute z-10 bg-white mt-[4px] rounded-xl ml-3 shadow-2xl w-64 right-[0px]">
<Popover.Panel className="absolute z-10 bg-white mt-[4px] rounded-xl ml-3 shadow-2xl w-64 md:right-[0px] max-md:right-[-300%]">
<div className="grid grid-cols-1">
{isNotifications && notificationsMap}
{!isNotifications &&
+4 -10
View File
@@ -74,17 +74,11 @@ export function ChatRoom({ roomObj, user }) {
<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>
<div className="width-[100%] grid grid-cols-6">
<input type="text" {...register("message")} placeholder="Enter message" className="col-span-5 border-[0px] mt-[8px] mb-[8px]" />
<button className="p-2 cursor-pointer bg-cyan-500 text-white font-bold rounded-full mr-5 w-[60px]">Send</button>
</div>
</Form>
</div>
</div>
@@ -23,7 +23,7 @@ export function ProfilePanel({user}) {
return (
<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="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
@@ -39,7 +39,7 @@ export function ProfilePanel({user}) {
<div className="grid grid-cols-1">
<Link
className="rounded-xl p-4 hover:bg-[#C0C0C0]"
href={"/user/" + user.uid}
href={"/user?uid=" + user.uid}
>
View Profile
</Link>
@@ -33,7 +33,7 @@ export function Sidebar({chatRoomObj}) {
var chatroomUsers = allUsers
}
return (
<div className="h-dvh">
<div className="overflow-hidden 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">
@@ -86,7 +86,6 @@ export function Sidebar({user,location,loadingLoc}) {
const [tab, setTab] = useState("nearby");
const [nearbyArr, setNearbyArr] = useState([])
const [nearbyArrReady, setNearbyArrReady] = useState(false)
// Add myRooms to Sidebar
var myRoomArr = [];
for (var room in user.rooms) {
@@ -124,27 +123,27 @@ export function Sidebar({user,location,loadingLoc}) {
}, [location])
return (
<div className="h-dvh">
<div className="bg-white shadow-2xl rounded-lg m-2 h-[98%]">
<div className="h-dvh bg-[aliceblue] pt-2 pb-2 pl-2 pr-1">
<div className="bg-white rounded-lg h-[98%] mb-[10px] mt-[-18px] mr-2">
<Tab.Group>
<Tab.List className="bg-[#D3D3D3] rounded-lg mt-5">
<Tab className={({ selected }) =>
classNames(
'w-[31%]',
'w-[30%]',
selected
? 'bg-cyan-500 text-white font-bold shadow hover:bg-white/[0.6] hover:text-black'
: 'hover:bg-cyan-500/[0.6] hover:text-white hover:font-bold'
)} defaultIndex={1}>Nearby</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[31%]',
'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'
)}>My Rooms</Tab>
<Tab className={({ selected }) =>
classNames(
'w-[31%]',
'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'