Added title bar and basic message and command input
This commit is contained in:
parent
508a04cf4c
commit
f42d6011c6
23 changed files with 399 additions and 83 deletions
|
@ -1,4 +1,5 @@
|
|||
var Reflux = require('reflux');
|
||||
|
||||
var sock = require('../socket.js')('/ws');
|
||||
|
||||
var channelActions = Reflux.createActions([
|
||||
|
@ -6,7 +7,9 @@ var channelActions = Reflux.createActions([
|
|||
'joined',
|
||||
'part',
|
||||
'parted',
|
||||
'quit',
|
||||
'setUsers',
|
||||
'setTopic',
|
||||
'load'
|
||||
]);
|
||||
|
||||
|
@ -26,10 +29,18 @@ sock.on('part', function(data) {
|
|||
channelActions.parted(data.user, data.server, data.channels[0]);
|
||||
});
|
||||
|
||||
sock.on('quit', function(data) {
|
||||
channelActions.quit(data.user, data.server);
|
||||
});
|
||||
|
||||
sock.on('users', function(data) {
|
||||
channelActions.setUsers(data.users, data.server, data.channel);
|
||||
});
|
||||
|
||||
sock.on('topic', function(data) {
|
||||
channelActions.setTopic(data.topic, data.server, data.channel);
|
||||
});
|
||||
|
||||
sock.on('channels', function(data) {
|
||||
channelActions.load(data);
|
||||
});
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
var Reflux = require('reflux');
|
||||
|
||||
var sock = require('../socket.js')('/ws');
|
||||
|
||||
var messageActions = Reflux.createActions([
|
||||
'send',
|
||||
'add',
|
||||
'selectTab'
|
||||
]);
|
||||
|
||||
messageActions.send.preEmit = function() {
|
||||
|
||||
messageActions.send.preEmit = function(message, to, server) {
|
||||
sock.send('chat', {
|
||||
server: server,
|
||||
to: to,
|
||||
message: message
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = messageActions;
|
|
@ -1,4 +1,5 @@
|
|||
var Reflux = require('reflux');
|
||||
|
||||
var sock = require('../socket.js')('/ws');
|
||||
|
||||
var serverActions = Reflux.createActions([
|
||||
|
|
|
@ -26,7 +26,7 @@ sock.on('connect', function() {
|
|||
|
||||
channelActions.join({
|
||||
server: 'irc.freenode.net',
|
||||
channels: [ '#stuff', '#go-nuts' ]
|
||||
channels: [ '#stuff' ]
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -35,7 +35,8 @@ channelActions.joined.listen(function(user, server, channel) {
|
|||
server: server,
|
||||
from: '',
|
||||
to: channel,
|
||||
message: user + ' joined the channel'
|
||||
message: user + ' joined the channel',
|
||||
type: 'info'
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -44,7 +45,8 @@ channelActions.parted.listen(function(user, server, channel) {
|
|||
server: server,
|
||||
from: '',
|
||||
to: channel,
|
||||
message: user + ' left the channel'
|
||||
message: user + ' left the channel',
|
||||
type: 'info'
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -56,15 +58,6 @@ sock.on('pm', function(data) {
|
|||
messageActions.add(data);
|
||||
});
|
||||
|
||||
sock.on('topic', function(data) {
|
||||
messageActions.add({
|
||||
server: data.server,
|
||||
from: '',
|
||||
to: data.channel,
|
||||
message: data.topic
|
||||
});
|
||||
});
|
||||
|
||||
sock.on('motd', function(data) {
|
||||
_.each(data.content.split('\n'), function(line) {
|
||||
messageActions.add({
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
var React = require('react');
|
||||
|
||||
var TabList = require('./TabList.jsx');
|
||||
var MessageBox = require('./MessageBox.jsx');
|
||||
var UserList = require('./UserList.jsx');
|
||||
var Chat = require('./Chat.jsx');
|
||||
|
||||
var App = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<TabList />
|
||||
<MessageBox />
|
||||
<UserList />
|
||||
<Chat />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
21
client/src/js/components/Chat.jsx
Normal file
21
client/src/js/components/Chat.jsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
var React = require('react');
|
||||
|
||||
var ChatTitle = require('./ChatTitle.jsx');
|
||||
var MessageBox = require('./MessageBox.jsx');
|
||||
var MessageInput = require('./MessageInput.jsx');
|
||||
var UserList = require('./UserList.jsx');
|
||||
|
||||
var Chat = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<ChatTitle />
|
||||
<MessageBox />
|
||||
<MessageInput />
|
||||
<UserList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Chat;
|
48
client/src/js/components/ChatTitle.jsx
Normal file
48
client/src/js/components/ChatTitle.jsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
var React = require('react');
|
||||
var Reflux = require('reflux');
|
||||
|
||||
var channelStore = require('../stores/channel.js');
|
||||
var selectedTabStore = require('../stores/selectedTab.js');
|
||||
|
||||
var ChatTitle = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(channelStore, 'channels'),
|
||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
channels: channelStore.getState(),
|
||||
selectedTab: selectedTabStore.getState()
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tab = this.state.selectedTab;
|
||||
var title;
|
||||
|
||||
if (tab.channel) {
|
||||
var channel = this.state.channels[tab.server][tab.channel];
|
||||
if (channel) {
|
||||
title = tab.channel
|
||||
title += ' [';
|
||||
title += channel.users.length;
|
||||
title += ']';
|
||||
|
||||
if (channel.topic) {
|
||||
title += ': ' + channel.topic;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
title = tab.server;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="chat-title-bar">
|
||||
<span className="chat-title" title={title}>{title}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ChatTitle;
|
|
@ -1,6 +1,7 @@
|
|||
var React = require('react');
|
||||
var Reflux = require('reflux');
|
||||
var _ = require('lodash');
|
||||
|
||||
var messageStore = require('../stores/message.js');
|
||||
var selectedTabStore = require('../stores/selectedTab.js');
|
||||
|
||||
|
@ -32,7 +33,13 @@ var MessageBox = React.createClass({
|
|||
render: function() {
|
||||
var tab = this.state.selectedTab.channel || this.state.selectedTab.server;
|
||||
var messages = _.map(this.state.messages[tab], function(message) {
|
||||
return <p>{message.from ? message.from + ': ' : null}{message.message}</p>;
|
||||
var messageClass = 'message';
|
||||
switch (message.type) {
|
||||
case 'info':
|
||||
messageClass += ' message-info';
|
||||
break;
|
||||
}
|
||||
return <p className={messageClass}>{message.from ? message.from + ': ' : null}{message.message}</p>;
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
65
client/src/js/components/MessageInput.jsx
Normal file
65
client/src/js/components/MessageInput.jsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
var React = require('react');
|
||||
var Reflux = require('reflux');
|
||||
|
||||
var selectedTabStore = require('../stores/selectedTab.js');
|
||||
var messageActions = require('../actions/message.js');
|
||||
var channelActions = require('../actions/channel.js');
|
||||
|
||||
function dispatchCommand(cmd, channel, server) {
|
||||
var params = cmd.slice(1).split(' ');
|
||||
|
||||
switch (params[0].toLowerCase()) {
|
||||
case 'join':
|
||||
if (params[1]) {
|
||||
channelActions.join({
|
||||
server: server,
|
||||
channels: [params[1]]
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'part':
|
||||
if (channel) {
|
||||
channelActions.part({
|
||||
server: server,
|
||||
channels: [channel]
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var MessageInput = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
selectedTab: selectedTabStore.getState()
|
||||
};
|
||||
},
|
||||
|
||||
handleKey: function(e) {
|
||||
if (e.which === 13 && e.target.value) {
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
if (e.target.value.charAt(0) === '/') {
|
||||
dispatchCommand(e.target.value, tab.channel, tab.server);
|
||||
} else {
|
||||
messageActions.send(e.target.value, tab.channel, tab.server);
|
||||
}
|
||||
e.target.value = '';
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="message-input-wrap">
|
||||
<input className="message-input" type="text" onKeyDown={this.handleKey} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MessageInput;
|
|
@ -1,6 +1,7 @@
|
|||
var React = require('react');
|
||||
var Reflux = require('reflux');
|
||||
var _ = require('lodash');
|
||||
|
||||
var channelStore = require('../stores/channel.js');
|
||||
var selectedTabStore = require('../stores/selectedTab.js');
|
||||
|
||||
|
@ -20,11 +21,14 @@ var UserList = React.createClass({
|
|||
render: function() {
|
||||
var users = null;
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
|
||||
if (tab.channel) {
|
||||
users = _.map(this.state.channels[tab.server][tab.channel].users, function(user) {
|
||||
return <p>{user}</p>;
|
||||
});
|
||||
var channel = this.state.channels[tab.server][tab.channel];
|
||||
if (channel) {
|
||||
users = _.map(channel.users, function(user) {
|
||||
return <p>{user}</p>;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
var sockets = {};
|
||||
|
@ -8,7 +9,7 @@ function createSocket(path) {
|
|||
return sockets[path];
|
||||
} else {
|
||||
var ws = new WebSocket('ws://' + window.location.host + path);
|
||||
|
||||
|
||||
var sock = {
|
||||
send: function(type, data) {
|
||||
ws.send(JSON.stringify({ type: type, request: data }));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var Reflux = require('reflux');
|
||||
var _ = require('lodash');
|
||||
|
||||
var actions = require('../actions/channel.js');
|
||||
|
||||
var channels = {};
|
||||
|
@ -36,16 +37,29 @@ var channelStore = Reflux.createStore({
|
|||
this.trigger(channels);
|
||||
},
|
||||
|
||||
quit: function(user, server) {
|
||||
_.each(channels[server], function(channel) {
|
||||
_.pull(channel.users, user);
|
||||
});
|
||||
this.trigger(channels);
|
||||
},
|
||||
|
||||
setUsers: function(users, server, channel) {
|
||||
initChannel(server, channel);
|
||||
channels[server][channel].users = users;
|
||||
this.trigger(channels);
|
||||
},
|
||||
|
||||
setTopic: function(topic, server, channel) {
|
||||
channels[server][channel].topic = topic;
|
||||
this.trigger(channels);
|
||||
},
|
||||
|
||||
load: function(storedChannels) {
|
||||
_.each(storedChannels, function(channel) {
|
||||
initChannel(channel.server, channel.name);
|
||||
channels[channel.server][channel.name].users = channel.users;
|
||||
channels[channel.server][channel.name].topic = channel.topic;
|
||||
});
|
||||
this.trigger(channels);
|
||||
},
|
||||
|
|
|
@ -1,25 +1,40 @@
|
|||
var Reflux = require('reflux');
|
||||
|
||||
var actions = require('../actions/message.js');
|
||||
|
||||
var messages = {};
|
||||
|
||||
function addMessage(message, dest) {
|
||||
if (!(dest in messages)) {
|
||||
messages[dest] = [message];
|
||||
} else {
|
||||
messages[dest].push(message);
|
||||
}
|
||||
}
|
||||
|
||||
var messageStore = Reflux.createStore({
|
||||
init: function() {
|
||||
this.listenToMany(actions);
|
||||
},
|
||||
|
||||
send: function(message, to, server) {
|
||||
addMessage({
|
||||
server: server,
|
||||
from: 'self',
|
||||
to: to,
|
||||
message: message
|
||||
}, to);
|
||||
|
||||
this.trigger(messages);
|
||||
},
|
||||
|
||||
add: function(message) {
|
||||
var dest = message.to || message.from;
|
||||
if (message.from.indexOf('.') !== -1) {
|
||||
dest = message.server;
|
||||
}
|
||||
|
||||
if (!(dest in messages)) {
|
||||
messages[dest] = [message];
|
||||
} else {
|
||||
messages[dest].push(message);
|
||||
}
|
||||
|
||||
addMessage(message, dest);
|
||||
this.trigger(messages);
|
||||
},
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
var Reflux = require('reflux');
|
||||
var _ = require('lodash');
|
||||
|
||||
var actions = require('../actions/tab.js');
|
||||
var channelActions = require('../actions/channel.js');
|
||||
|
||||
var selectedTab = {};
|
||||
|
||||
var selectedTabStore = Reflux.createStore({
|
||||
init: function() {
|
||||
this.listenToMany(actions);
|
||||
this.listenTo(channelActions.part, 'part');
|
||||
},
|
||||
|
||||
select: function(server, channel) {
|
||||
|
@ -14,6 +18,19 @@ var selectedTabStore = Reflux.createStore({
|
|||
this.trigger(selectedTab);
|
||||
},
|
||||
|
||||
part: function(data) {
|
||||
var self = this;
|
||||
if (data.server === selectedTab.server) {
|
||||
_.each(data.channels, function(channel) {
|
||||
if (channel === selectedTab.channel) {
|
||||
delete selectedTab.channel;
|
||||
self.trigger(selectedTab);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getState: function() {
|
||||
return selectedTab;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
var Reflux = require('reflux');
|
||||
|
||||
var actions = require('../actions/server.js');
|
||||
|
||||
var servers = {};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -9,6 +10,14 @@ body {
|
|||
color: #FFF;
|
||||
}
|
||||
|
||||
input {
|
||||
font: 16px Inconsolata, sans-serif;
|
||||
background: rgba(0,0,0,0.25);
|
||||
color: #FFF;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
@ -19,7 +28,7 @@ p {
|
|||
top: 0;
|
||||
bottom: 0;
|
||||
right: 200px;
|
||||
padding: 20px;
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
@ -31,23 +40,64 @@ p {
|
|||
color: #AAA;
|
||||
}
|
||||
|
||||
.messagebox {
|
||||
.chat-title-bar {
|
||||
position: fixed;
|
||||
left: 200px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 200px;
|
||||
padding: 20px;
|
||||
height: 50px;
|
||||
padding: 0 15px;
|
||||
line-height: 50px;
|
||||
background: rgba(0,0,0,0.25);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.chat-title {
|
||||
|
||||
}
|
||||
|
||||
.messagebox {
|
||||
position: fixed;
|
||||
left: 200px;
|
||||
top: 50px;
|
||||
bottom: 50px;
|
||||
right: 200px;
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.message {
|
||||
|
||||
}
|
||||
|
||||
.message-info {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.message-input-wrap {
|
||||
position: fixed;
|
||||
left: 200px;
|
||||
bottom: 0px;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.message-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.userlist {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
bottom: 50px;
|
||||
right: 0;
|
||||
width: 200px;
|
||||
padding: 20px;
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue