diff --git a/src/App.tsx b/src/App.tsx index db5f7cc..c7e3eae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route } from 'react-router'; import { IonApp, IonRouterOutlet } from '@ionic/react'; import { IonReactRouter } from '@ionic/react-router'; import Home from './pages/Home'; diff --git a/src/components/Chatbox.css b/src/components/Chatbox.css deleted file mode 100644 index b45dde6..0000000 --- a/src/components/Chatbox.css +++ /dev/null @@ -1,52 +0,0 @@ -ion-textarea { - border: solid 1px #999; -} - -.chat-card { - background: var(--ion-color-light); - height: 100%; - width: 100%; - float: right; - margin: 0; -} - -.message-toolbar { - padding-left: 5px; - --background: var(--ion-color-light); -} - -.footer-ios ion-toolbar:first-of-type { - padding-top: 5px; -} - -.send-msg { - padding-left: 0; -} - -.send-button { - align-self: center; - margin: 0 2px 0 5px; - --box-shadow: 0 3px 2px -1px rgba(0, 0, 0, 0.2), 0 2px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} - -.message-input { - width: 100%; - height: 100%; - border: 1px solid #999; - border-radius: 5px; - text-align: left; -} - -@media (max-width: 576px) { - .message-input { - border-radius: 10px; - } - - .send-msg { - display: none; - } - - .native-input.sc-ion-input-ios { - padding-inline-start: 5px; - } -} diff --git a/src/components/Chatbox.tsx b/src/components/Chatbox.tsx deleted file mode 100644 index 4adcc97..0000000 --- a/src/components/Chatbox.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { IonCard, IonFabButton, IonFooter, IonIcon, IonInput, IonToolbar } from '@ionic/react'; -import { sendOutline } from 'ionicons/icons'; -import React, { useState } from 'react'; -import { rtdb } from '../services/firebase'; -import './Chatbox.css'; -import Messages from './Messages'; -import OnlineList from './OnlineList'; - -type ChatboxProps = { - ownerId: string; - roomId: string; - userId: string; - userList: Map; -}; - -const Chat: React.FC = ({ ownerId, roomId, userId, userList }) => { - const [message, setMessage] = useState(''); // Message to be sent - - // Send message to database - const sendMessage = async () => { - if (message !== '') { - await rtdb.ref('/chats/' + roomId).push({ - content: message, - createdAt: Date.now(), - senderId: userId, - }); - - // Reset textarea field - setMessage(''); - } - }; - - const onEnter = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - sendMessage(); - } - }; - - return ( - - - - - setMessage(e.detail.value!)} - onKeyDown={(e) => onEnter(e)} - value={message} - placeholder="Send message" - enterkeyhint="send" - class="message-input" - > - - - - - - - - ); -}; - -export default Chat; diff --git a/src/components/Frame.css b/src/components/Frame.css new file mode 100644 index 0000000..7326f61 --- /dev/null +++ b/src/components/Frame.css @@ -0,0 +1,12 @@ +.frame-card { + background: var(--ion-color-light); + height: 100%; + width: 100%; + float: right; + margin: 0; +} + +ion-segment-button { + --indicator-transition: transform 100ms cubic-bezier(0.4, 0, 0.2, 1); + max-height: 40px; +} diff --git a/src/components/Frame.tsx b/src/components/Frame.tsx new file mode 100644 index 0000000..76fecb3 --- /dev/null +++ b/src/components/Frame.tsx @@ -0,0 +1,38 @@ +import { IonCard, IonIcon, IonSegment, IonSegmentButton } from '@ionic/react'; +import { chatboxOutline, informationCircleOutline, peopleOutline } from 'ionicons/icons'; +import React, { useState } from 'react'; +import './Frame.css'; +import Messages from './Messages'; +import OnlineList from './OnlineList'; + +type FrameProps = { + ownerId: string; + roomId: string; + userId: string; + userList: Map; +}; + +const Frame: React.FC = ({ ownerId, roomId, userId, userList }) => { + const [pane, setPane] = useState('chat'); + + return ( + + + setPane('chat')}> + + + setPane('online')}> + + + setPane('about')}> + + + + + + + + ); +}; + +export default Frame; diff --git a/src/components/Messages.css b/src/components/Messages.css index bb1e88f..92c2c2f 100644 --- a/src/components/Messages.css +++ b/src/components/Messages.css @@ -1,6 +1,6 @@ -.message-card { +.message-content { overflow-y: auto; - height: calc(100% - 125px); + height: calc(100% - 160px); padding: 0; } @@ -31,3 +31,40 @@ background: var(--ion-color-secondary); color: #fff; } + +.message-toolbar { + padding-left: 5px; + --background: var(--ion-color-light); +} + +.footer-ios ion-toolbar:first-of-type { + padding-top: 5px; +} + +ion-textarea { + border: solid 1px #999; +} + +.message-input { + width: 100%; + height: 100%; + border: 1px solid #999; + border-radius: 5px; + text-align: left; +} + +.send-button { + align-self: center; + margin: 0 5px 0 5px; + --box-shadow: 0 3px 2px -1px rgba(0, 0, 0, 0.2), 0 2px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); +} + +@media (max-width: 576px) { + .message-input { + border-radius: 10px; + } + + .native-input.sc-ion-input-ios { + padding-inline-start: 5px; + } +} diff --git a/src/components/Messages.tsx b/src/components/Messages.tsx index b575150..551448c 100644 --- a/src/components/Messages.tsx +++ b/src/components/Messages.tsx @@ -1,10 +1,22 @@ -import { IonCol, IonContent, IonGrid, IonRow } from '@ionic/react'; +import { + IonCol, + IonContent, + IonFabButton, + IonFooter, + IonGrid, + IonIcon, + IonInput, + IonRow, + IonToolbar, +} from '@ionic/react'; +import { sendOutline } from 'ionicons/icons'; import React, { useEffect, useRef, useState } from 'react'; -import { db, arrayUnion, rtdb } from '../services/firebase'; -import './Messages.css'; +import { arrayUnion, db, rtdb } from '../services/firebase'; import { secondsToTimestamp } from '../services/utilities'; +import './Messages.css'; type MessagesProps = { + pane: string; ownerId: string; roomId: string; userId: string; @@ -18,12 +30,13 @@ type Message = { senderId: string; }; -const Messages: React.FC = ({ ownerId, roomId, userId, userList }) => { +const Messages: React.FC = ({ pane, ownerId, roomId, userId, userList }) => { const [joinTime] = useState(Date.now()); // Time at mounting of the component const [chats, setChats] = useState([]); // All processed chat messages const [systemMessages, setSystemMessages] = useState([]); // All processed system messages const [allMessages, setAllMessages] = useState([]); // Combined array of chat and system messages const [userHistory] = useState>(new Map()); // All users who are/were in the room + const [message, setMessage] = useState(''); // Message to be sent const contentRef = useRef(null); // Send 'joined room' message on component mount @@ -41,12 +54,14 @@ const Messages: React.FC = ({ ownerId, roomId, userId, userList } let arr: Message[] = []; snapshot.forEach((child) => { const msg = child.val(); - arr.push({ - content: msg.content, - createdAt: msg.createdAt, - id: msg.senderId + msg.createdAt, - senderId: msg.senderId, - }); + if (msg.createdAt > joinTime) { + arr.push({ + content: msg.content, + createdAt: msg.createdAt, + id: msg.senderId + msg.createdAt, + senderId: msg.senderId, + }); + } }); setChats(arr); }); @@ -133,6 +148,26 @@ const Messages: React.FC = ({ ownerId, roomId, userId, userList } return name; }; + // Send message to database + const sendMessage = async () => { + if (message !== '') { + await rtdb.ref('/chats/' + roomId).push({ + content: message, + createdAt: Date.now(), + senderId: userId, + }); + + // Reset textarea field + setMessage(''); + } + }; + + const onEnter = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + sendMessage(); + } + }; + const renderMessages = () => { return allMessages .sort((msg1, msg2) => msg1.createdAt - msg2.createdAt) @@ -153,9 +188,26 @@ const Messages: React.FC = ({ ownerId, roomId, userId, userList } }; return ( - - {userList.size === 0 ? Loading... : renderMessages()} - + <> + + {userList.size === 0 ? Loading... : renderMessages()} + + + + setMessage(e.detail.value!)} + onKeyDown={(e) => onEnter(e)} + value={message} + placeholder="Send message" + enterkeyhint="send" + class="message-input" + > + + + + + + ); }; diff --git a/src/components/OnlineList.css b/src/components/OnlineList.css index a7f27f8..49a0807 100644 --- a/src/components/OnlineList.css +++ b/src/components/OnlineList.css @@ -1,22 +1,16 @@ -.online-button { - margin: 0 4px 0 0; - align-self: center; - --box-shadow: 0 3px 2px -1px rgba(0, 0, 0, 0.2), 0 2px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); +.online-content { + height: calc(100% - 160px); + padding: 0; } -.popover-list { +.online-list { padding-top: 2px; } -.list-header { - font-size: 24px; - color: var(--ion-color-primary); - border-bottom: 1px solid #999; -} - .online-item { --padding-start: 12px; --min-height: 0; + color: var(--ion-color-primary); } .online-label { diff --git a/src/components/OnlineList.tsx b/src/components/OnlineList.tsx index 8cb65e6..10f4446 100644 --- a/src/components/OnlineList.tsx +++ b/src/components/OnlineList.tsx @@ -1,47 +1,25 @@ -import React, { useState } from 'react'; -import { IonPopover, IonFabButton, IonIcon, IonList, IonListHeader, IonItem, IonLabel } from '@ionic/react'; -import { peopleOutline } from 'ionicons/icons'; +import { IonContent, IonItem, IonLabel, IonList } from '@ionic/react'; +import React from 'react'; import './OnlineList.css'; type OnlineListProps = { + pane: string; userList: Map; }; -const OnlineList: React.FC = ({ userList }) => { - const [showPopover, setShowPopover] = useState<{ open: boolean; event: Event | undefined }>({ - open: false, - event: undefined, - }); - +const OnlineList: React.FC = ({ pane, userList }) => { return ( - <> - setShowPopover({ open: false, event: undefined })} - > - - Online - {Array.from(userList.values()).map((user) => { - return ( - - {user} - - ); - })} - - - setShowPopover({ open: true, event: e.nativeEvent })} - > - - - + + + {Array.from(userList.values()).map((user) => { + return ( + + {user} + + ); + })} + + ); }; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 851b1b9..47a5823 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -22,7 +22,7 @@ const Home: React.FC = () => { }); await db.collection('playlists').doc(roomId.id).set({ createdAt: timestamp, - url: 'https://www.youtube.com/watch?v=XEfDYMngJeE', + url: 'https://www.youtube.com/watch?v=DGQwd1_dpuc', }); await db.collection('states').doc(roomId.id).set({ isPlaying: false, @@ -32,7 +32,6 @@ const Home: React.FC = () => { // RealTimeDB preparations await rtdb.ref('/rooms/' + roomId.id).set({ userCount: 0 }); await rtdb.ref('/available/' + roomId.id).set({ name: 'Room Name', createdAt: new Date().toISOString() }); - await rtdb.ref('/chats/' + roomId.id).push({ content: 'created a room.', createdAt: Date.now(), senderId: userId }); const path = '/room/' + roomId.id; return history.push(path); diff --git a/src/pages/Room.css b/src/pages/Room.css index a9502f5..82e22e3 100644 --- a/src/pages/Room.css +++ b/src/pages/Room.css @@ -23,12 +23,12 @@ height: 100%; } -.chat-col { +.frame-col { padding: 0; } @media (max-width: 992px) { - .chat-col { + .frame-col { height: 65%; } } diff --git a/src/pages/Room.tsx b/src/pages/Room.tsx index c65a18e..e9fa3c5 100644 --- a/src/pages/Room.tsx +++ b/src/pages/Room.tsx @@ -1,7 +1,7 @@ import { IonCol, IonContent, IonGrid, IonHeader, IonPage, IonRow } from '@ionic/react'; import React, { useEffect, useState } from 'react'; import { RouteComponentProps, useHistory } from 'react-router'; -import Chat from '../components/Chatbox'; +import Frame from '../components/Frame'; import RoomHeader from '../components/RoomHeader'; import VideoPlayer from '../components/VideoPlayer'; import { auth, db, decrement, increment, rtdb } from '../services/firebase'; @@ -155,8 +155,8 @@ const Room: React.FC> = ({ match }) => { - - + +