Skip to content

Commit 3d9fc04

Browse files
committed
Finish websocket chat on server and in app
1 parent 34bfc95 commit 3d9fc04

File tree

4 files changed

+136
-28
lines changed

4 files changed

+136
-28
lines changed

chat-server/index.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const app = express();
77
// apply websockets to express
88
setupWs(app);
99

10-
const clients = [];
10+
// chat sessions storage
1111
const chatSessions = {};
1212

1313
// setup route
@@ -18,24 +18,37 @@ app.ws('/:series/:episode', (ws, req) => {
1818
const chatKey = series + episode;
1919

2020
if (!chatSessions[chatKey]) {
21-
chatSessions[chatKey] = [];
21+
chatSessions[chatKey] = {
22+
clients: [],
23+
messages: [],
24+
};
2225
}
26+
// alias it
27+
const session = chatSessions[chatKey];
2328

2429
// assign index and store in memory db
2530
ws.uuid = clientId;
26-
clients.push(ws);
31+
session.clients.push(ws);
2732

2833
ws.on('message', msg => {
2934
console.log(msg);
35+
session.messages.push(msg);
36+
session.clients.forEach(w => w.send(msg));
3037
});
3138

3239
ws.on('close', () => {
33-
const clientIndex = clients.findIndex(c => c.uuid === clientId);
34-
clients.splice(clientIndex, 1);
35-
console.log('Client disconnected:', clientId, clients);
40+
const clientIndex = session.clients.findIndex(c => c.uuid === clientId);
41+
session.clients.splice(clientIndex, 1);
42+
console.log('Client disconnected:', clientId, session.clients);
43+
if (session.clients.length === 0) {
44+
session.messages = [];
45+
}
3646
});
3747

38-
console.log('New connection for', series, episode, 'with client:', clientId, '\n', clients);
48+
console.log('New connection for', series, episode, 'with client:', clientId, '\n', session.clients);
49+
session.messages.forEach(msg => ws.send(msg));
3950
});
4051

41-
app.listen(3000);
52+
app.listen(3000, () => {
53+
console.log('listening on :3000');
54+
});

src/api/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ import Youtube from './youtube';
55
// manager
66
import PluginManager from './manager';
77

8-
export default new PluginManager([Crunchyroll, Youtube]);
8+
export default new PluginManager([Crunchyroll]); //, Youtube]);

src/components/chat/index.js

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
// npm packages
22
import React from 'react';
33

4+
// our packages
5+
import Message from './message';
6+
47
export default class Chat extends React.Component {
58
constructor(props) {
69
super(props);
710

811
this.state = {
912
episode: props.episode,
13+
connected: false,
14+
username: undefined,
15+
messages: [],
1016
};
1117
}
1218

1319
componentWillReceiveProps(nextProps) {
1420
if (nextProps.episode && (!this.state.episode || nextProps.episode._id !== this.state.episode._id)) {
15-
this.setState({episode: nextProps.episode}, () => {
21+
this.setState({episode: nextProps.episode, messages: [], connected: false}, () => {
1622
this.connectToServer();
1723
});
1824
}
1925
}
2026

27+
setUsername() {
28+
const username = this.usernameInput.value;
29+
this.setState({username});
30+
}
31+
2132
connectToServer() {
2233
const {episode} = this.state;
2334

@@ -26,34 +37,96 @@ export default class Chat extends React.Component {
2637
this.socket = new WebSocket(url);
2738

2839
// Connection opened
29-
// this.socket.addEventListener('open', event => {
30-
// this.socket.send('Hello Server!');
31-
// });
40+
this.socket.addEventListener('open', () => this.setState({connected: true}));
3241

3342
// Listen for messages
3443
this.socket.addEventListener('message', event => {
35-
console.log('Message from server', event.data);
44+
const message = JSON.parse(event.data);
45+
console.log('Message from server', message);
46+
const {messages: oldMessages} = this.state;
47+
const messages = oldMessages.concat([message]).sort((a, b) => a.date < b.date);
48+
this.setState({messages});
3649
});
3750
}
3851

52+
sendMessage() {
53+
const message = this.messageInput.value;
54+
55+
if (!message || message.length < 3) {
56+
return;
57+
}
58+
59+
this.messageInput.value = '';
60+
this.socket.send(JSON.stringify({user: this.state.username, message, date: new Date()}));
61+
}
62+
63+
handleMessageKey(e) {
64+
if (e.key === 'Enter') {
65+
e.preventDefault();
66+
this.sendMessage();
67+
return false;
68+
}
69+
70+
return true;
71+
}
72+
3973
render() {
74+
const {username, messages, connected} = this.state;
75+
76+
if (!connected) {
77+
return (
78+
<div className="column" style={{width: 340, maxWidth: 340, display: 'flex', flexDirection: 'column'}}>
79+
Connecting..
80+
</div>
81+
);
82+
}
83+
4084
return (
4185
<div className="column" style={{width: 340, maxWidth: 340, display: 'flex', flexDirection: 'column'}}>
42-
<div className="is-flex" style={{flexGrow: 1}}>
43-
Chat history
44-
</div>
45-
<div className="is-flex">
46-
<div className="field has-addons" style={{flexGrow: 1}}>
47-
<p className="control" style={{flexGrow: 1}}>
48-
<input className="input" type="text" placeholder="Send a message.." />
49-
</p>
50-
<p className="control" style={{marginRight: 10}}>
51-
<a className="button is-info">
52-
Send
53-
</a>
54-
</p>
55-
</div>
86+
<div className="is-flex" style={{flexGrow: 1, flexDirection: 'column'}}>
87+
{messages.map((m, i) => <Message key={`msg_${i}`} message={m} />)}
5688
</div>
89+
{username &&
90+
<div className="is-flex">
91+
<div className="field has-addons" style={{flexGrow: 1}}>
92+
<p className="control" style={{flexGrow: 1}}>
93+
<input
94+
className="input"
95+
type="text"
96+
placeholder="Send a message.."
97+
ref={m => {
98+
this.messageInput = m;
99+
}}
100+
onKeyUp={e => this.handleMessageKey(e)}
101+
/>
102+
</p>
103+
<p className="control" style={{marginRight: 10}}>
104+
<a className="button is-info" onClick={() => this.sendMessage()}>
105+
Send
106+
</a>
107+
</p>
108+
</div>
109+
</div>}
110+
{!username &&
111+
<div className="is-flex">
112+
<div className="field has-addons" style={{flexGrow: 1}}>
113+
<p className="control" style={{flexGrow: 1}}>
114+
<input
115+
className="input"
116+
type="text"
117+
placeholder="Pick a username.."
118+
ref={u => {
119+
this.usernameInput = u;
120+
}}
121+
/>
122+
</p>
123+
<p className="control" style={{marginRight: 10}}>
124+
<a className="button is-primary" onClick={() => this.setUsername()}>
125+
Set username
126+
</a>
127+
</p>
128+
</div>
129+
</div>}
57130
</div>
58131
);
59132
}

src/components/chat/message.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// npm packages
2+
import React from 'react';
3+
4+
export default ({message}) => {
5+
const date = new Date(message.date);
6+
7+
return (
8+
<article className="media">
9+
<div className="media-content">
10+
<div className="content">
11+
<p>
12+
<strong>{message.user}</strong>
13+
{' '}
14+
<small>{date.toLocaleTimeString()} {date.toLocaleDateString()}</small>
15+
<br />
16+
{message.message}
17+
</p>
18+
</div>
19+
</div>
20+
</article>
21+
);
22+
};

0 commit comments

Comments
 (0)