diff options
author | Logan Hunt <loganhunt@simponic.xyz> | 2022-03-30 22:15:20 -0600 |
---|---|---|
committer | Logan Hunt <loganhunt@simponic.xyz> | 2022-03-30 22:15:20 -0600 |
commit | 42cf50ee7521bd751f4d0f0798276e548bb83fee (patch) | |
tree | 5b2e8877a137a9b62ea630cc69135183804131d5 /client/components | |
parent | ae0c829453d4663203887979349047850cb98626 (diff) | |
download | locchat-42cf50ee7521bd751f4d0f0798276e548bb83fee.tar.gz locchat-42cf50ee7521bd751f4d0f0798276e548bb83fee.zip |
Working
Diffstat (limited to 'client/components')
-rw-r--r-- | client/components/chatroom/_chat_room.jsx | 86 | ||||
-rw-r--r-- | client/components/common/input.jsx | 2 | ||||
-rw-r--r-- | client/components/home/_home.jsx | 11 | ||||
-rw-r--r-- | client/components/home/ping.jsx | 84 | ||||
-rw-r--r-- | client/components/map/_map.jsx | 4 | ||||
-rw-r--r-- | client/components/map/chat_room_geoman.jsx | 37 | ||||
-rw-r--r-- | client/components/map/legend.jsx | 3 | ||||
-rw-r--r-- | client/components/router.jsx | 2 |
8 files changed, 125 insertions, 104 deletions
diff --git a/client/components/chatroom/_chat_room.jsx b/client/components/chatroom/_chat_room.jsx new file mode 100644 index 0000000..e70715c --- /dev/null +++ b/client/components/chatroom/_chat_room.jsx @@ -0,0 +1,86 @@ +import { useEffect, useState, useContext } from 'react'; +import { ApiContext } from '../../utils/api_context'; +import { useMessages } from '../../utils/use_messages'; +import { Link, useParams } from 'react-router-dom'; +import { generateGruvboxFromString } from '../../utils/generate_gruvbox'; + +/* + A lot of this is stolen from my Docker presentation :). + https://github.com/USUFSLC/sochat +*/ +export const ChatRoom = () => { + const { id } = useParams(); + const [chatRoom, setChatRoom] = useState(''); + const [messages, sendMessage] = useMessages(chatRoom); + const [message, setMessage] = useState(''); + const [color, setColor] = useState(generateGruvboxFromString('placeholder')); + const [user, setUser] = useState({}); + const api = useContext(ApiContext); + + const fetchUser = async () => { + const res = await api.get('/users/me'); + if (res.user) { + setUser(res.user); + setColor(generateGruvboxFromString(`${res.user.firstName} ${res.user.lastName}`)); + } + }; + + const fetchChatRoom = async (id) => { + const room = await api.get(`/chat_rooms/${id}`); + if (room) { + setChatRoom(room); + } + }; + + const scrollToBottomOfChat = () => { + const objDiv = document.getElementById('chat'); + objDiv.scrollTop = objDiv.scrollHeight; + }; + + const sendThisMessage = () => { + sendMessage(message); + setMessage(''); + }; + + useEffect(() => { + fetchUser(); + fetchChatRoom(id); + }, [id]); + + useEffect(() => { + scrollToBottomOfChat(); + }, [messages]); + + return ( + <div className="container" style={{ border: `1px solid ${color}` }}> + <div style={{ textAlign: 'center' }}> + <h2>{chatRoom?.name || `Chat Room ${chatRoom?.id}`}</h2> + </div> + <div id="chat" className="chat"> + <p>Welcome!</p> + {messages.map((message) => ( + <div key={message.id} style={{ lineBreak: 'normal' }}> + <span style={{ color: generateGruvboxFromString(message.userName) }}>{message.userName}: </span> + <span>{message.content}</span> + </div> + ))} + </div> + <div> + <textarea + placeholder={'Message'} + className="input" + onChange={(e) => setMessage(e.target.value)} + value={message} + rows={1} + cols={30} + ></textarea> + <div className="button" onClick={sendThisMessage}> + Send + </div> + <div className="button"> + <Link to="/">Back to map</Link> + </div> + </div> + </div> + ); +}; diff --git a/client/components/common/input.jsx b/client/components/common/input.jsx index aa38216..2c237e8 100644 --- a/client/components/common/input.jsx +++ b/client/components/common/input.jsx @@ -1,3 +1,3 @@ export const Input = (props) => { - return <input className="border-2 rounded-lg p-1" {...props} />; + return <input className="border-2 rounded-lg p-1 input" {...props} />; }; diff --git a/client/components/home/_home.jsx b/client/components/home/_home.jsx index 7ef051c..213d43e 100644 --- a/client/components/home/_home.jsx +++ b/client/components/home/_home.jsx @@ -5,13 +5,11 @@ import { AuthContext } from '../../utils/auth_context'; import { RolesContext } from '../../utils/roles_context'; import { Button } from '../common/button'; import { Map } from '../map/_map'; -import { Ping } from './ping'; export const Home = () => { const [, setAuthToken] = useContext(AuthContext); const api = useContext(ApiContext); const roles = useContext(RolesContext); - const navigate = useNavigate(); const [loading, setLoading] = useState(true); @@ -29,6 +27,13 @@ export const Home = () => { } }; + const joinRoom = async (id, userPosition) => { + const res = await api.get(`/chat_rooms/${id}/joinable?lat=${userPosition.lat}&lng=${userPosition.lng}`); + if (res) { + navigate(`/rooms/${id}`); + } + }; + if (loading) { return <div>Loading...</div>; } @@ -46,7 +51,7 @@ export const Home = () => { </Button> )} </div> - <Map user={user} /> + <Map user={user} joinRoom={joinRoom} /> </> ); }; diff --git a/client/components/home/ping.jsx b/client/components/home/ping.jsx deleted file mode 100644 index 6166921..0000000 --- a/client/components/home/ping.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useState, useEffect, useRef, useContext } from 'react'; -import { Button } from '../common/button'; -import { io } from 'socket.io-client'; -import { AuthContext } from '../../utils/auth_context'; - -export const Ping = () => { - const [pings, setPings] = useState([]); - const [key, setKey] = useState('defaultkey'); - const [currentRoom, setCurrentRoom] = useState(null); - const [authToken] = useContext(AuthContext); - const [socket, setSocket] = useState(null); - - useEffect(() => { - // instantiates a socket object and initiates the connection... - // you probably want to make sure you are only doing this in one component at a time. - const socket = io({ - auth: { token: authToken }, - query: { message: 'I am the query ' }, - }); - - // adds an event listener to the connection event - socket.on('connect', () => { - setSocket(socket); - }); - - // adds event listener to the disconnection event - socket.on('disconnect', () => { - console.log('Disconnected'); - }); - - // recieved a pong event from the server - socket.on('pong', (data) => { - console.log('Recieved pong', data); - }); - - // IMPORTANT! Unregister from all events when the component unmounts and disconnect. - return () => { - socket.off('connect'); - socket.off('disconnect'); - socket.off('pong'); - socket.disconnect(); - }; - }, []); - - useEffect(() => { - // if our token changes we need to tell the socket also - if (socket) { - // this is a little weird because we are modifying this object in memory - // i dunno a better way to do this though... - socket.auth.token = authToken; - } - }, [authToken]); - - if (!socket) return 'Loading...'; - - const sendPing = () => { - // sends a ping to the server to be broadcast to everybody in the room - currentRoom && socket.emit('ping', { currentRoom }); - }; - - const joinRoom = () => { - // tells the server to remove the current client from the current room and add them to the new room - socket.emit('join-room', { currentRoom, newRoom: key }, (response) => { - console.log(response); - setCurrentRoom(response.room); - }); - }; - - return ( - <> - <header>Ping: {currentRoom || '(No room joined)'}</header> - <section> - <input - type="text" - className="border-2 border-gray-700 p-2 rounded" - value={key} - onChange={(e) => setKey(e.target.value)} - /> - <Button onClick={joinRoom}>Connect To Room</Button> - <Button onClick={sendPing}>Send Ping</Button> - </section> - </> - ); -}; diff --git a/client/components/map/_map.jsx b/client/components/map/_map.jsx index 9f6684c..6134d44 100644 --- a/client/components/map/_map.jsx +++ b/client/components/map/_map.jsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'; import toast from 'react-hot-toast'; import { Legend } from './legend'; -export const Map = ({ user, zoom }) => { +export const Map = ({ user, zoom, joinRoom }) => { const [loading, setLoading] = useState(true); const [position, setPosition] = useState({}); const [positionWatcher, setPositionWatcher] = useState(); @@ -39,7 +39,7 @@ export const Map = ({ user, zoom }) => { maxZoom={19} /> <Legend /> - <Geoman joinRoom={console.log} userPos={position} user={user} /> + <Geoman joinRoom={joinRoom} userPos={position} user={user} /> </MapContainer> ); } diff --git a/client/components/map/chat_room_geoman.jsx b/client/components/map/chat_room_geoman.jsx index f59fec8..9f7ab6a 100644 --- a/client/components/map/chat_room_geoman.jsx +++ b/client/components/map/chat_room_geoman.jsx @@ -1,16 +1,15 @@ import { useLeafletContext } from '@react-leaflet/core'; import L from 'leaflet'; import markerIconPng from 'leaflet/dist/images/marker-icon.png'; -import { useEffect, useContext } from 'react'; +import { useEffect, useContext, useState } from 'react'; import { ApiContext } from '../../utils/api_context'; const userPositionBubble = { color: 'black', fillColor: 'black', - fillOpacity: 0.6, - weight: 5, + fillOpacity: 0.4, + weight: 1, pmIgnore: true, - radius: 5, }; const joinable = { @@ -31,7 +30,7 @@ const editable = { pmIgnore: false, }; -const icon = new L.Icon({ iconUrl: markerIconPng, iconSize: [25, 41], iconAnchor: [12, 41] }); +const icon = new L.Icon({ iconUrl: markerIconPng, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [0, -30] }); const haversine = (p1, p2) => { const degreesToRadians = (degrees) => degrees * (Math.PI / 180); @@ -51,6 +50,7 @@ const haversine = (p1, p2) => { export const Geoman = ({ user, userPos, joinRoom }) => { const context = useLeafletContext(); const api = useContext(ApiContext); + let dontRedirect = true; const circleAndMarkerFromChatroom = (chatRoom) => { const circle = new L.Circle(chatRoom.center, chatRoom.radius); const marker = new L.Marker(chatRoom.center, { pmIgnore: !chatRoom.isEditable, icon }); @@ -62,10 +62,15 @@ export const Geoman = ({ user, userPos, joinRoom }) => { : unjoinable, ); marker.addEventListener('click', () => { - console.log(chatRoom.id); - console.log(haversine(userPos, { lat: chatRoom.latitude, lng: chatRoom.longitude }), chatRoom.radius, userPos); + setTimeout(() => { + if (dontRedirect) { + joinRoom(chatRoom.id, userPos); + return; + } + dontRedirect = false; + }, 500); }); - if (!!chatRoom.isEditable) { + if (chatRoom.isEditable) { [circle, marker].map((x) => { x.on('pm:edit', (e) => { const coords = e.target.getLatLng(); @@ -78,6 +83,7 @@ export const Geoman = ({ user, userPos, joinRoom }) => { }); }); x.on('pm:remove', (e) => { + dontRedirect = true; context.map.removeLayer(marker); context.map.removeLayer(circle); @@ -87,9 +93,17 @@ export const Geoman = ({ user, userPos, joinRoom }) => { circle.on('pm:drag', (e) => { marker.setLatLng(e.target.getLatLng()); }); + marker.bindPopup(chatRoom.name || `Chat Room ${chatRoom.id}`); + marker.on('mouseover', (e) => { + console.log(chatRoom); + e.target.openPopup(); + }); marker.on('pm:drag', (e) => { circle.setLatLng(e.target.getLatLng()); }); + marker.on('pm:dragstart', (e) => { + dontRedirect = true; + }); } [circle, marker].map((x) => x.addTo(context.map)); return [circle, marker]; @@ -149,19 +163,16 @@ export const Geoman = ({ user, userPos, joinRoom }) => { const { lat: latitude, lng: longitude } = shape.layer.getLatLng(); const chatRoom = await api.post('/chat_rooms', { + name: prompt("What's the name of the chat room?"), latitude, longitude, radius: shape.layer.getRadius(), }); + console.log(chatRoom); reRender(); } }); - leafletContainer.on('pm:remove', (e) => { - console.log('object removed'); - // console.log(leafletContainer.pm.getGeomanLayers(true).toGeoJSON()); - }); - return () => { leafletContainer.pm.removeControls(); leafletContainer.pm.setGlobalOptions({ pmIgnore: true }); diff --git a/client/components/map/legend.jsx b/client/components/map/legend.jsx index ebd199d..14f6536 100644 --- a/client/components/map/legend.jsx +++ b/client/components/map/legend.jsx @@ -2,6 +2,7 @@ import L from 'leaflet'; import { useEffect } from 'react'; import { useLeafletContext } from '@react-leaflet/core'; +/* Legend adapted from https://codesandbox.io/s/how-to-add-a-legend-to-the-map-using-react-leaflet-6yqs5 */ export const Legend = () => { const context = useLeafletContext(); useEffect(() => { @@ -14,7 +15,7 @@ export const Legend = () => { labels.push('<i style="background:black"></i><span>Current position</span>'); labels.push('<i style="background:red"></i><span>Unjoinable</span>'); labels.push('<i style="background:green"></i><span>Joinable</span>'); - labels.push('<i style="background:blue"></i><span>Editable</span>'); + labels.push('<i style="background:blue"></i><span>Editable & Joinable</span>'); div.innerHTML = labels.join('<br>'); return div; diff --git a/client/components/router.jsx b/client/components/router.jsx index 08bb41f..71aabc2 100644 --- a/client/components/router.jsx +++ b/client/components/router.jsx @@ -5,6 +5,7 @@ import { AuthContext } from '../utils/auth_context'; import { SignIn } from './sign_in/_sign_in'; import { SignUp } from './sign_up/_sign_up'; import { Admin } from './admin/_admin'; +import { ChatRoom } from './chatroom/_chat_room'; export const Router = () => { const [authToken] = useContext(AuthContext); @@ -18,6 +19,7 @@ export const Router = () => { <Route path="admin" element={<Admin />} /> <Route path="signin" element={<SignIn />} /> <Route path="signup" element={<SignUp />} /> + <Route path="rooms/:id" element={<ChatRoom />} /> </Routes> ); }; |