IRC output gets queued until RPL_WELCOME, added tcp timeouts and error handling, store selected tab in localStorage, more design work, upgraded to lodash 3.0.0
This commit is contained in:
parent
5c6c43e017
commit
3c02b00303
18 changed files with 268 additions and 53 deletions
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title>IRC</title>
|
||||
<link href='http://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
|
||||
<link href="//fonts.googleapis.com/css?family=Montserrat|Roboto" rel="stylesheet">
|
||||
<link href="style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -8,11 +8,12 @@ var serverActions = Reflux.createActions([
|
|||
'load'
|
||||
]);
|
||||
|
||||
serverActions.connect.preEmit = function(server, nick, username) {
|
||||
serverActions.connect.preEmit = function(server, nick, username, tls) {
|
||||
socket.send('connect', {
|
||||
server: server,
|
||||
nick: nick,
|
||||
username: username
|
||||
username: username,
|
||||
tls: tls || false
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -14,8 +14,14 @@ socket.on('connect', function() {
|
|||
socket.send('uuid', uuid);
|
||||
|
||||
serverActions.connect('irc.freenode.net', nick, 'username');
|
||||
serverActions.connect('irc.quakenet.org', nick, 'username');
|
||||
|
||||
channelActions.join(['#stuff'], 'irc.freenode.net');
|
||||
tabActions.select('irc.freenode.net');
|
||||
channelActions.join(['#herp'], 'irc.quakenet.org');
|
||||
});
|
||||
|
||||
socket.on('error', function(error) {
|
||||
console.log(error.server + ': ' + error.message);
|
||||
});
|
||||
|
||||
React.render(<App />, document.body);
|
|
@ -20,18 +20,15 @@ var ChatTitle = React.createClass({
|
|||
render: function() {
|
||||
var tab = this.state.selectedTab;
|
||||
var title;
|
||||
var topic;
|
||||
var usercount;
|
||||
|
||||
if (tab.channel) {
|
||||
if (tab.channel && this.state.channels[tab.server]) {
|
||||
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;
|
||||
}
|
||||
usercount = channel.users.length;
|
||||
topic = channel.topic || '';
|
||||
}
|
||||
} else {
|
||||
title = tab.server;
|
||||
|
@ -39,7 +36,11 @@ var ChatTitle = React.createClass({
|
|||
|
||||
return (
|
||||
<div className="chat-title-bar">
|
||||
<span className="chat-title" title={title}>{title}</span>
|
||||
<div>
|
||||
<span className="chat-title">{title}</span>
|
||||
<span className="chat-topic" title={topic}>{topic}</span>
|
||||
</div>
|
||||
<span className="chat-usercount">{usercount}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ var React = require('react');
|
|||
var Reflux = require('reflux');
|
||||
var _ = require('lodash');
|
||||
|
||||
var util = require('../util');
|
||||
var messageStore = require('../stores/message');
|
||||
var selectedTabStore = require('../stores/selectedTab');
|
||||
|
||||
|
@ -38,12 +39,20 @@ var MessageBox = React.createClass({
|
|||
if (this.state.messages[tab.server] && dest) {
|
||||
messages = _.map(this.state.messages[tab.server][dest], function(message) {
|
||||
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 (
|
||||
<p className={messageClass}>
|
||||
<span className="message-time">{util.timestamp(message.time)}</span>
|
||||
{ message.from ? <span className="message-sender">{message.from}</span> : null }
|
||||
{message.message}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -21,16 +21,41 @@ var TabList = React.createClass({
|
|||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var tabClass;
|
||||
var selected = this.state.selectedTab;
|
||||
|
||||
var tabs = _.map(this.state.channels, function(server, address) {
|
||||
var channels = _.map(server, function(channel, name) {
|
||||
return <p onClick={tabActions.select.bind(null, address, name)}>{name}</p>;
|
||||
var channels = _.map(server, function(channel, name) {
|
||||
if (address === selected.server &&
|
||||
name === selected.channel) {
|
||||
tabClass = 'selected';
|
||||
} else {
|
||||
tabClass = '';
|
||||
}
|
||||
|
||||
return <p className={tabClass} onClick={tabActions.select.bind(null, address, name)}>{name}</p>;
|
||||
});
|
||||
channels.unshift(<p onClick={tabActions.select.bind(null, address, null)}>{address}</p>);
|
||||
|
||||
if (address === selected.server &&
|
||||
selected.channel === null) {
|
||||
tabClass = 'tab-server selected';
|
||||
} else {
|
||||
tabClass = 'tab-server';
|
||||
}
|
||||
|
||||
channels.unshift(<p className={tabClass} onClick={tabActions.select.bind(null, address, null)}>{address}</p>);
|
||||
|
||||
return channels;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="tablist">{tabs}</div>
|
||||
<div className="tablist">
|
||||
<button className="button-connect">Add Network</button>
|
||||
{tabs}
|
||||
<div className="side-buttons">
|
||||
<button>Settings</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ var UserList = React.createClass({
|
|||
var users = null;
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
if (tab.channel) {
|
||||
if (tab.channel && this.state.channels[tab.server]) {
|
||||
var channel = this.state.channels[tab.server][tab.channel];
|
||||
if (channel) {
|
||||
users = _.map(channel.users, function(user) {
|
||||
|
|
|
@ -2,6 +2,7 @@ var Reflux = require('reflux');
|
|||
var _ = require('lodash');
|
||||
|
||||
var actions = require('../actions/channel');
|
||||
var serverActions = require('../actions/server');
|
||||
|
||||
var channels = {};
|
||||
|
||||
|
@ -80,6 +81,7 @@ function sortUsers(server, channel) {
|
|||
var channelStore = Reflux.createStore({
|
||||
init: function() {
|
||||
this.listenToMany(actions);
|
||||
this.listenTo(serverActions.connect, 'addServer');
|
||||
},
|
||||
|
||||
part: function(partChannels, server) {
|
||||
|
@ -161,6 +163,13 @@ var channelStore = Reflux.createStore({
|
|||
this.trigger(channels);
|
||||
},
|
||||
|
||||
addServer: function(server) {
|
||||
if (!(server in channels)) {
|
||||
channels[server] = {};
|
||||
this.trigger(channels);
|
||||
}
|
||||
},
|
||||
|
||||
getState: function() {
|
||||
return channels;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var Reflux = require('reflux');
|
||||
|
||||
var serverStore = require('../stores/server');
|
||||
var serverStore = require('./server');
|
||||
var actions = require('../actions/message');
|
||||
|
||||
var messages = {};
|
||||
|
@ -26,7 +26,8 @@ var messageStore = Reflux.createStore({
|
|||
server: server,
|
||||
from: serverStore.getNick(server),
|
||||
to: to,
|
||||
message: message
|
||||
message: message,
|
||||
time: new Date()
|
||||
}, to);
|
||||
|
||||
this.trigger(messages);
|
||||
|
@ -38,6 +39,8 @@ var messageStore = Reflux.createStore({
|
|||
dest = message.server;
|
||||
}
|
||||
|
||||
message.time = new Date();
|
||||
|
||||
addMessage(message, dest);
|
||||
this.trigger(messages);
|
||||
},
|
||||
|
|
|
@ -5,6 +5,11 @@ var actions = require('../actions/tab');
|
|||
var channelActions = require('../actions/channel');
|
||||
|
||||
var selectedTab = {};
|
||||
var stored = localStorage.selectedTab;
|
||||
|
||||
if (stored) {
|
||||
selectedTab = JSON.parse(stored);
|
||||
}
|
||||
|
||||
var selectedTabStore = Reflux.createStore({
|
||||
init: function() {
|
||||
|
@ -36,4 +41,8 @@ var selectedTabStore = Reflux.createStore({
|
|||
}
|
||||
});
|
||||
|
||||
selectedTabStore.listen(function(selected) {
|
||||
localStorage.selectedTab = JSON.stringify(selected);
|
||||
});
|
||||
|
||||
module.exports = selectedTabStore;
|
|
@ -1,6 +1,17 @@
|
|||
var _ = require('lodash');
|
||||
|
||||
exports.UUID = function() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
};
|
||||
|
||||
exports.timestamp = function(date) {
|
||||
date = date || new Date();
|
||||
|
||||
var h = _.padLeft(date.getHours(), 2, '0')
|
||||
var m = _.padLeft(date.getMinutes(), 2, '0');
|
||||
|
||||
return h + ':' + m;
|
||||
};
|
|
@ -5,17 +5,24 @@
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: Inconsolata, sans-serif;
|
||||
font-family: Roboto, sans-serif;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
input {
|
||||
font: 16px Inconsolata, sans-serif;
|
||||
font: 16px Roboto, sans-serif;
|
||||
border: none;
|
||||
border-top: 1px solid #DDD;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font: 16px Montserrat, sans-serif;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
@ -26,20 +33,64 @@ p {
|
|||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
background: #272626;
|
||||
background: #222;
|
||||
color: #FFF;
|
||||
font-family: Montserrat, sans-serif;
|
||||
}
|
||||
|
||||
.tablist p {
|
||||
padding: 3px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tablist p:hover {
|
||||
background: #111;
|
||||
}
|
||||
|
||||
.tablist p.selected {
|
||||
padding-left: 10px;
|
||||
border-left: 5px solid #6BB758;
|
||||
}
|
||||
|
||||
.tab-server {
|
||||
color: #AAA;
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.button-connect {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background: #6BB758;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.button-connect:hover {
|
||||
background: #7BBF6A;
|
||||
}
|
||||
|
||||
.button-connect:active {
|
||||
background: #6BB758;
|
||||
}
|
||||
|
||||
.side-buttons {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.side-buttons button {
|
||||
background: #333;
|
||||
color: #FFF;
|
||||
margin: 5px;
|
||||
height: 40px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.chat-title-bar {
|
||||
font-family: Montserrat, sans-serif;
|
||||
position: fixed;
|
||||
left: 200px;
|
||||
top: 0;
|
||||
|
@ -47,14 +98,34 @@ p {
|
|||
height: 50px;
|
||||
padding: 0 15px;
|
||||
line-height: 50px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
|
||||
.chat-title-bar div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 100px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-title {
|
||||
font-size: 24px;
|
||||
margin-left: 15px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.chat-topic {
|
||||
display: none;
|
||||
font: 16px Roboto, sans-serif;
|
||||
line-height: 50px;
|
||||
vertical-align: top;
|
||||
color: #222;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.chat-usercount {
|
||||
font-size: 20px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.messagebox {
|
||||
|
@ -69,11 +140,25 @@ p {
|
|||
}
|
||||
|
||||
.message {
|
||||
padding-left: 50px;
|
||||
text-indent: -50px;
|
||||
}
|
||||
|
||||
.message span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.message-info {
|
||||
color: #666;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.message-sender {
|
||||
color: #6BB758;
|
||||
font: 16px Montserrat, sans-serif;
|
||||
}
|
||||
|
||||
.message-input-wrap {
|
||||
|
@ -100,4 +185,5 @@ p {
|
|||
padding: 15px;
|
||||
overflow: auto;
|
||||
border-left: 1px solid #DDD;
|
||||
overflow-x: hidden;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue