Skip to content

Commit

Permalink
Add waiting room and display room actions in the chat (#22)
Browse files Browse the repository at this point in the history
* Add waiting and public room

* start activity log

* Finish activity log

* Merged with waiting room logic and fix chat overflow and playlist header
  • Loading branch information
1234momo authored May 1, 2021
1 parent 43cdf77 commit e3de679
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 82 deletions.
10 changes: 8 additions & 2 deletions client/src/components/CreateSessionForm.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import React from 'react';
import { Form, Input, Button } from 'antd';
import { Form, Input, Button, Radio } from 'antd';

const CreateSessionForm = ({
startSession,
}: {
startSession: ({ displayName }: { displayName: string }) => void;
startSession: ({ displayName, roomType }: { displayName: string, roomType: string }) => void;
}) => {
return (
<Form layout="vertical" onFinish={startSession}>
<Form.Item label="Display Name" name="displayName">
<Input placeholder="Please enter your display name for new session" />
</Form.Item>
<Form.Item label="Room type" name="roomType" initialValue="public">
<Radio.Group defaultValue="public">
<Radio value="public">Public</Radio>
<Radio value="private">Private</Radio>
</Radio.Group>
</Form.Item>
<Form.Item>
<Button type="primary" shape="round" htmlType="submit">
Start Session
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const Chat = (props: ChatProps) => {

<Form form={form} layout="inline" onFinish={onSend}>
<Form.Item name="message" className={chatStyles.message__input}>
<Input placeholder="Send a message..." autoComplete="off" />
<Input placeholder="Send a message..." autoComplete="off" required />
</Form.Item>
<Form.Item className={chatStyles.send__btn}>
<Button type="primary" shape="round" htmlType="submit">
Expand Down
15 changes: 8 additions & 7 deletions client/src/components/chat/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface MessagesProps {
interface MessageProps {
children: string;
user: string;
key: string;
key: number;
}

interface Message {
Expand Down Expand Up @@ -62,8 +62,8 @@ const Messages = (props: MessagesProps) => {
>
{messages &&
messages.length > 0 &&
messages.map((message: Message) => (
<Message user={message.client} key={message.clientId}>
messages.map((message: Message, index: number) => (
<Message user={message.client} key={index}>
{message.message}
</Message>
))}
Expand All @@ -87,7 +87,7 @@ const Message = (props: MessageProps) => {
>
<div
style={{
fontWeight: 'normal',
fontWeight: 'bold',
fontSize: '0.85em',
marginBottom: '3px',
marginLeft: '8px',
Expand All @@ -114,16 +114,17 @@ const Message = (props: MessageProps) => {
display: 'flex',
flexDirection: 'column',
fontSize: '0.9em',
marginBottom: '10px',
marginBottom: '2px',
boxSizing: 'border-box',
// if admin, text-align: center, color: grey
}}
>
<div
style={{
padding: '8px 10px',
padding: '5px 10px',
fontWeight: 'normal',
fontSize: '0.9em',
fontSize: '0.78rem',
color: '#787878',
backgroundColor: 'transparent', // or #eee if admin
width: 'auto', // or 80% if admin
borderRadius: '15px',
Expand Down
6 changes: 4 additions & 2 deletions client/src/pages/Landing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ const { TabPane } = Tabs;
const Landing = (props: RouteComponentProps & any) => {
const { setClientId, setClientDisplayName } = useContext(SocketContext);

const startSession = async ({ displayName }: { displayName: string }) => {
const newSocket = await createConnection(displayName);
const startSession = async (
{ displayName, roomType }: { displayName: string, roomType: string }
) => {
const newSocket = await createConnection(displayName, roomType, true);

setClientId(newSocket.id);
setClientDisplayName(displayName);
Expand Down
165 changes: 141 additions & 24 deletions client/src/pages/Room.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useContext } from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { RouteComponentProps, withRouter, useHistory } from 'react-router-dom';
import { createConnection, roomSocketEvents } from '../utils/socket-client';
import { SocketContext } from '../App';
import Video from '../components/Video';
Expand All @@ -11,7 +11,7 @@ import Playlist from '../components/Playlist';
import RoomParticipants from '../components/RoomParticipants';
import { MediasoupPeer } from '../utils/MediasoupPeer';

import { Row, Col, Modal, Form, Input, Button, Select } from 'antd';
import { Row, Col, Modal, Form, Input, Button, Select, Alert, Space, notification, Spin } from 'antd';
import roomStyles from '../styles/pages/room.module.scss';
const { Option } = Select;

Expand Down Expand Up @@ -53,10 +53,12 @@ const Room = ({ location, match }: RoomProps & any) => {
? [{ id: clientId, name: clientDisplayName, isMuted: false }]
: []
);
const [waitingClients, setWaitingClients] = useState<{[socketId: string]: string}>({});

const [enterDisplayName, setEnterDisplayName] = useState(false);
const [isMuted, setIsMuted] = useState(false);
const [selectNewAudio, setSelectNewAudio] = useState(false);
const [canEnter, setCanEnter] = useState(clientId ? true : false);

const [socket, setSocket] = useState(location.socket ? location.socket : {});
const [mediasoupPeer, setMediasoupPeer] = useState<MediasoupPeer>();
Expand All @@ -73,6 +75,7 @@ const Room = ({ location, match }: RoomProps & any) => {
clientDispatch,
videoDispatch,
};
const history = useHistory();
const remoteAudiosDiv = document.getElementById('remoteAudios');

useEffect(() => {
Expand All @@ -87,38 +90,49 @@ const Room = ({ location, match }: RoomProps & any) => {
type: ClientStates.UPDATE_YOUTUBE_ID,
youtubeID: roomYoutubeId,
});
setCanEnter(true);
setHostEvents(location.socket);
setWaitingRoomEvents(location.socket);
updateClientList(location.socket);
setSocket(location.socket);
roomSocketEvents(location.socket, dispatches);
await startAudioCall(location.socket);
}
// Joining client or Room host that already made connection and refreshed
else if (clientId && clientDisplayName) {
// Joining client from landing, inputted displayName, or Room client
// that already made connection and refreshed
else if ((!clientId && clientDisplayName) || (clientId && clientDisplayName)) {
const { roomId } = match.params;
const socketConnection = await createConnection(
clientDisplayName,
'',
canEnter,
roomId
);
setClientId(socketConnection.id);
updateClientList(socketConnection);
updatePlaylist(socketConnection);
setSocket(socketConnection);
roomSocketEvents(socketConnection, dispatches);
await startAudioCall(socketConnection);
}
// Joining clients: inputted displayName
else if (!clientId && clientDisplayName) {
const { roomId } = match.params;
const socketConnection = await createConnection(
clientDisplayName,
roomId
);
setClientId(socketConnection.id);
updateClientList(socketConnection);
updatePlaylist(socketConnection);
setSocket(socketConnection);
roomSocketEvents(socketConnection, dispatches);
await startAudioCall(socketConnection);

if (canEnter) {
await enterRoom(socketConnection);
}
else {
socketConnection.on('accept', async () => {
rejoinSocket(roomId, socketConnection);
setCanEnter(true);
await enterRoom(socketConnection);
});
socketConnection.on('decline', () => history.push('/'));

const callback = async (roomType: string) => {
if (roomType !== 'private') {
rejoinSocket(roomId, socketConnection);
await enterRoom(socketConnection);
}
};

socketConnection.emit('getRoomType', {
clientName: clientDisplayName,
roomId
}, callback);
}
}
// Joining client from direct URL, prompt for displayName
else if (!clientId && !clientDisplayName) {
Expand Down Expand Up @@ -147,6 +161,63 @@ const Room = ({ location, match }: RoomProps & any) => {
setEnterDisplayName(false);
};

const rejoinSocket = (roomId: string, connectingSocket: SocketIOClient.Socket) => {
setCanEnter(true);
const clientData = {
roomId,
clientId: connectingSocket.id,
clientName: clientDisplayName,
roomType: '',
canJoin: true
};

connectingSocket.emit('join', clientData);
};

const setWaitingRoomEvents = (connectingSocket: SocketIOClient.Socket) => {
connectingSocket.on('newHost', (hostName: string, hostId: string) => {
notification.open({
message: 'New Host',
description:
`${connectingSocket.id !== hostId ? `${hostName} is` : 'You are'} now the host`,
duration: 8
});

if (connectingSocket.id === hostId) {
setHostEvents(connectingSocket);
}
});
};

const setHostEvents = (connectingSocket: SocketIOClient.Socket) => {
connectingSocket.on('waitingClient', (
{ socketId, clientName }: {socketId: string, clientName: string}
) => {
setWaitingClients({
...waitingClients,
[socketId]: clientName
});
});

connectingSocket.on('updateWaitingClients', (
{ waitingClientList, clientIdLeft }: {
waitingClientList: {[socketId: string]: string},
clientIdLeft: string
}) => {
setWaitingClients(waitingClientList);
if (clientIdLeft.length > 0) notification.close(clientIdLeft);
});
};

const enterRoom = async (connectingSocket: SocketIOClient.Socket) => {
setWaitingRoomEvents(connectingSocket);
setClientId(connectingSocket.id);
updateClientList(connectingSocket);
updatePlaylist(connectingSocket);
roomSocketEvents(connectingSocket, dispatches);
await startAudioCall(connectingSocket);
};

const startAudioCall = async (socket: SocketIOClient.Socket) => {
const deviceId = await setMediaDevices();
await startMediasoup(socket, deviceId);
Expand Down Expand Up @@ -219,6 +290,14 @@ const Room = ({ location, match }: RoomProps & any) => {
setSelectNewAudio(!selectNewAudio);
};

const handleNotification = (socketId: string, status: string) => {
const newWaitingClients = waitingClients;
delete newWaitingClients[socketId];
setWaitingClients(newWaitingClients);
socket.emit('waitingResponse', { socketId, status });
notification.close(socketId);
};

return (
<div className={`${roomStyles.root} container`}>
{enterDisplayName ? (
Expand All @@ -245,6 +324,11 @@ const Room = ({ location, match }: RoomProps & any) => {
</Form.Item>
</Form>
</Modal>
) : !canEnter ? (
<div className={roomStyles.waiting}>
<h1>Please wait while the host lets you in</h1>
<Spin size="large"/>
</div>
) : (
<div>
<div id="remoteAudios" />
Expand Down Expand Up @@ -274,6 +358,39 @@ const Room = ({ location, match }: RoomProps & any) => {
</div>
</Modal>
)}
{Object.keys(waitingClients).map((socketId) => (
notification.open({
key: socketId,
message: 'Waiting Room Notification',
description: (
<Alert
message=""
description={`${waitingClients[socketId]} wants to join the room`}
action={
<Space direction="vertical">
<Button
size="small"
type="primary"
onClick={() => handleNotification(socketId, 'accept')}
>
Accept
</Button>
<Button
size="small"
danger type="ghost"
onClick={() => handleNotification(socketId, 'decline')}
>
Decline
</Button>
</Space>
}
/>
),
placement: 'topRight',
duration: 0,
closeIcon : (<div/>),
})
))}

<Row gutter={16} className={roomStyles.main__content}>
<Col sm={16} className={roomStyles.left__col}>
Expand All @@ -286,7 +403,7 @@ const Room = ({ location, match }: RoomProps & any) => {
<Video youtubeID={clientData.youtubeID} socket={socket} />
<Playlist socket={socket} />
</Col>
<Col sm={8}>
<Col sm={8} className={roomStyles.chat__col}>
<Chat socket={socket} />
</Col>
</Row>
Expand Down
13 changes: 12 additions & 1 deletion client/src/styles/pages/room.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

.main__content {
height: 100vh;
width: 100%;
width: auto;
padding: 1rem 0;
}

Expand All @@ -22,3 +22,14 @@
display: flex;
justify-content: center;
}

.waiting {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}

.chat__col {
max-height: 100%;
}
6 changes: 4 additions & 2 deletions client/src/utils/socket-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const socketServerDomain = 'http://localhost:5000';
// Establishes a WebSocket connection and resolves the socket object
export const createConnection = (
displayName: string,
roomType: string,
canJoin: boolean,
roomId?: string
): Promise<SocketIOClient.Socket> => {
return new Promise((resolve) => {
Expand All @@ -17,6 +19,8 @@ export const createConnection = (
// if a clientID is present in sessionStorage, use it again
clientId: socket.id,
clientName: displayName,
roomType,
canJoin
};

socket.emit('join', clientData);
Expand Down Expand Up @@ -72,8 +76,6 @@ export const roomSocketEvents = (
}
break;

// current issue: case 'newMessage' never invoked
// nothing happens even when commented out
case 'clientMessage':
clientDispatch({
type: ClientStates.UPDATE_CHAT_MESSAGES,
Expand Down
Loading

0 comments on commit e3de679

Please sign in to comment.