Immutable messages, search and selectedTab
This commit is contained in:
parent
77c723344c
commit
11ea241b60
@ -27,6 +27,8 @@
|
|||||||
"react-router": "0.13.3",
|
"react-router": "0.13.3",
|
||||||
"react": "0.13.3",
|
"react": "0.13.3",
|
||||||
"react-infinite": "0.3.4",
|
"react-infinite": "0.3.4",
|
||||||
"autolinker": "khlieng/Autolinker.js"
|
"autolinker": "khlieng/Autolinker.js",
|
||||||
|
"immutable": "~3.7.2",
|
||||||
|
"react-pure-render": "~1.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ var App = React.createClass({
|
|||||||
Reflux.listenTo(routeActions.navigate, 'navigate')
|
Reflux.listenTo(routeActions.navigate, 'navigate')
|
||||||
],
|
],
|
||||||
|
|
||||||
navigate: function(path, replace) {
|
navigate(path, replace) {
|
||||||
if (!replace) {
|
if (!replace) {
|
||||||
this.transitionTo(path);
|
this.transitionTo(path);
|
||||||
} else {
|
} else {
|
||||||
@ -21,7 +21,7 @@ var App = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<TabList />
|
<TabList />
|
||||||
|
@ -9,20 +9,22 @@ var MessageInput = require('./MessageInput.jsx');
|
|||||||
var UserList = require('./UserList.jsx');
|
var UserList = require('./UserList.jsx');
|
||||||
var selectedTabStore = require('../stores/selectedTab');
|
var selectedTabStore = require('../stores/selectedTab');
|
||||||
var tabActions = require('../actions/tab');
|
var tabActions = require('../actions/tab');
|
||||||
|
var PureMixin = require('../mixins/pure');
|
||||||
|
|
||||||
var Chat = React.createClass({
|
var Chat = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
|
PureMixin,
|
||||||
Router.State,
|
Router.State,
|
||||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
selectedTab: selectedTabStore.getState()
|
selectedTab: selectedTabStore.getState()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount() {
|
||||||
if (!window.loaded) {
|
if (!window.loaded) {
|
||||||
var p = this.getParams();
|
var p = this.getParams();
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ var Chat = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var chatClass;
|
var chatClass;
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ var ChatTitle = React.createClass({
|
|||||||
Reflux.listenTo(selectedTabStore, 'selectedTabChanged')
|
Reflux.listenTo(selectedTabStore, 'selectedTabChanged')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
var tab = selectedTabStore.getState();
|
var tab = selectedTabStore.getState();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -23,20 +23,20 @@ var ChatTitle = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
channelsChanged: function() {
|
channelsChanged() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
|
|
||||||
this.setState({ usercount: channelStore.getUsers(tab.server, tab.channel).length });
|
this.setState({ usercount: channelStore.getUsers(tab.server, tab.channel).length });
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedTabChanged: function(tab) {
|
selectedTabChanged(tab) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedTab: tab,
|
selectedTab: tab,
|
||||||
usercount: channelStore.getUsers(tab.server, tab.channel).length
|
usercount: channelStore.getUsers(tab.server, tab.channel).length
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleLeaveClick: function() {
|
handleLeaveClick() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
|
|
||||||
if (!tab.channel) {
|
if (!tab.channel) {
|
||||||
@ -48,7 +48,7 @@ var ChatTitle = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
var leaveTitle;
|
var leaveTitle;
|
||||||
|
|
||||||
|
@ -5,13 +5,13 @@ var serverActions = require('../actions/server');
|
|||||||
var channelActions = require('../actions/channel');
|
var channelActions = require('../actions/channel');
|
||||||
|
|
||||||
var Connect = React.createClass({
|
var Connect = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
showOptionals: false
|
showOptionals: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSubmit: function(e) {
|
handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var address = e.target.address.value.trim();
|
var address = e.target.address.value.trim();
|
||||||
|
@ -5,17 +5,20 @@ var Infinite = require('react-infinite');
|
|||||||
var Autolinker = require('autolinker');
|
var Autolinker = require('autolinker');
|
||||||
|
|
||||||
var MessageHeader = require('./MessageHeader.jsx');
|
var MessageHeader = require('./MessageHeader.jsx');
|
||||||
|
var MessageLine = require('./MessageLine.jsx');
|
||||||
var messageLineStore = require('../stores/messageLine');
|
var messageLineStore = require('../stores/messageLine');
|
||||||
var selectedTabStore = require('../stores/selectedTab');
|
var selectedTabStore = require('../stores/selectedTab');
|
||||||
var messageActions = require('../actions/message');
|
var messageActions = require('../actions/message');
|
||||||
|
var PureMixin = require('../mixins/pure');
|
||||||
|
|
||||||
var MessageBox = React.createClass({
|
var MessageBox = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
|
PureMixin,
|
||||||
Reflux.connect(messageLineStore, 'messages'),
|
Reflux.connect(messageLineStore, 'messages'),
|
||||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
messages: messageLineStore.getState(),
|
messages: messageLineStore.getState(),
|
||||||
selectedTab: selectedTabStore.getState(),
|
selectedTab: selectedTabStore.getState(),
|
||||||
@ -23,20 +26,20 @@ var MessageBox = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUpdate: function() {
|
componentWillUpdate() {
|
||||||
var el = this.refs.list.getDOMNode();
|
var el = this.refs.list.getDOMNode();
|
||||||
this.autoScroll = el.scrollTop + el.offsetHeight === el.scrollHeight;
|
this.autoScroll = el.scrollTop + el.offsetHeight === el.scrollHeight;
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
this.updateWidth();
|
this.updateWidth();
|
||||||
|
|
||||||
if (this.autoScroll) {
|
if (this.autoScroll) {
|
||||||
@ -45,12 +48,12 @@ var MessageBox = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleResize: function() {
|
handleResize() {
|
||||||
this.updateWidth();
|
this.updateWidth();
|
||||||
this.setState({ height: window.innerHeight - 100 });
|
this.setState({ height: window.innerHeight - 100 });
|
||||||
},
|
},
|
||||||
|
|
||||||
updateWidth: function() {
|
updateWidth() {
|
||||||
var width = this.refs.list.getDOMNode().firstChild.offsetWidth;
|
var width = this.refs.list.getDOMNode().firstChild.offsetWidth;
|
||||||
|
|
||||||
if (this.width !== width) {
|
if (this.width !== width) {
|
||||||
@ -59,7 +62,7 @@ var MessageBox = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
var dest = tab.channel || tab.server;
|
var dest = tab.channel || tab.server;
|
||||||
var lines = [];
|
var lines = [];
|
||||||
@ -67,27 +70,17 @@ var MessageBox = React.createClass({
|
|||||||
paddingLeft: this.props.indent + 'px'
|
paddingLeft: this.props.indent + 'px'
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var j = 0; j < this.state.messages.length; j++) {
|
this.state.messages.forEach((message, j) => {
|
||||||
var message = this.state.messages[j];
|
var key = message.server + dest + j;
|
||||||
var messageClass = 'message';
|
|
||||||
var key = message.server + dest + j;
|
|
||||||
|
|
||||||
if (message.type) {
|
|
||||||
messageClass += ' message-' + message.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push(<MessageHeader key={key} message={message} />);
|
lines.push(<MessageHeader key={key} message={message} />);
|
||||||
|
|
||||||
for (var i = 1; i < message.lines.length; i++) {
|
for (var i = 1; i < message.lines.length; i++) {
|
||||||
var line = Autolinker.link(message.lines[i], { keepOriginalText: true });
|
|
||||||
|
|
||||||
lines.push(
|
lines.push(
|
||||||
<p key={key + '-' + i} className={messageClass} style={innerStyle}>
|
<MessageLine key={key + '-' + i} type={message.type} line={message.lines[i]} />
|
||||||
<span dangerouslySetInnerHTML={{ __html: line }}></span>
|
|
||||||
</p>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (lines.length !== 1) {
|
if (lines.length !== 1) {
|
||||||
return (
|
return (
|
||||||
|
@ -7,14 +7,18 @@ var privateChatActions = require('../actions/privateChat');
|
|||||||
var tabActions = require('../actions/tab');
|
var tabActions = require('../actions/tab');
|
||||||
|
|
||||||
var MessageHeader = React.createClass({
|
var MessageHeader = React.createClass({
|
||||||
handleSenderClick: function() {
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return nextProps.message.lines[0] !== this.props.message.lines[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSenderClick() {
|
||||||
var message = this.props.message;
|
var message = this.props.message;
|
||||||
|
|
||||||
privateChatActions.open(message.server, message.from);
|
privateChatActions.open(message.server, message.from);
|
||||||
tabActions.select(message.server, message.from);
|
tabActions.select(message.server, message.from);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var message = this.props.message;
|
var message = this.props.message;
|
||||||
var sender = null;
|
var sender = null;
|
||||||
var messageClass = 'message';
|
var messageClass = 'message';
|
||||||
|
@ -6,33 +6,33 @@ var selectedTabStore = require('../stores/selectedTab');
|
|||||||
var messageActions = require('../actions/message');
|
var messageActions = require('../actions/message');
|
||||||
var inputHistoryActions = require('../actions/inputHistory');
|
var inputHistoryActions = require('../actions/inputHistory');
|
||||||
var tabActions = require('../actions/tab');
|
var tabActions = require('../actions/tab');
|
||||||
|
var PureMixin = require('../mixins/pure');
|
||||||
|
|
||||||
var MessageInput = React.createClass({
|
var MessageInput = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
Reflux.connect(selectedTabStore, 'selectedTab'),
|
PureMixin,
|
||||||
Reflux.connect(inputHistoryStore, 'history'),
|
Reflux.connect(inputHistoryStore, 'history'),
|
||||||
Reflux.listenTo(tabActions.select, 'tabSelected')
|
Reflux.listenTo(tabActions.select, 'tabSelected')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
selectedTab: selectedTabStore.getState(),
|
|
||||||
history: inputHistoryStore.getState(),
|
history: inputHistoryStore.getState(),
|
||||||
value: ''
|
value: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.refs.input.getDOMNode().focus();
|
this.refs.input.getDOMNode().focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
tabSelected: function() {
|
tabSelected() {
|
||||||
this.refs.input.getDOMNode().focus();
|
this.refs.input.getDOMNode().focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleKey: function(e) {
|
handleKey(e) {
|
||||||
if (e.which === 13 && e.target.value) {
|
if (e.which === 13 && e.target.value) {
|
||||||
var tab = this.state.selectedTab;
|
var tab = selectedTabStore.getState();
|
||||||
|
|
||||||
if (e.target.value[0] === '/') {
|
if (e.target.value[0] === '/') {
|
||||||
messageActions.command(e.target.value, tab.channel, tab.server);
|
messageActions.command(e.target.value, tab.channel, tab.server);
|
||||||
@ -55,11 +55,11 @@ var MessageInput = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleChange: function(e) {
|
handleChange(e) {
|
||||||
this.setState({ value: e.target.value });
|
this.setState({ value: e.target.value });
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="message-input-wrap">
|
<div className="message-input-wrap">
|
||||||
<input
|
<input
|
||||||
|
28
client/src/js/components/MessageLine.jsx
Normal file
28
client/src/js/components/MessageLine.jsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
var React = require('react');
|
||||||
|
var Autolinker = require('autolinker');
|
||||||
|
|
||||||
|
var PureMixin = require('../mixins/pure');
|
||||||
|
|
||||||
|
var MessageLine = React.createClass({
|
||||||
|
mixins: [PureMixin],
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var line = Autolinker.link(this.props.line, { keepOriginalText: true });
|
||||||
|
var messageClass = 'message';
|
||||||
|
var style = {
|
||||||
|
paddingLeft: window.messageIndent + 'px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.props.type) {
|
||||||
|
messageClass += ' message-' + this.props.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p className={messageClass} style={style}>
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: line }}></span>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = MessageLine;
|
@ -6,27 +6,29 @@ var util = require('../util');
|
|||||||
var searchStore = require('../stores/search');
|
var searchStore = require('../stores/search');
|
||||||
var selectedTabStore = require('../stores/selectedTab');
|
var selectedTabStore = require('../stores/selectedTab');
|
||||||
var searchActions = require('../actions/search');
|
var searchActions = require('../actions/search');
|
||||||
|
var PureMixin = require('../mixins/pure');
|
||||||
|
|
||||||
var Search = React.createClass({
|
var Search = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
Reflux.connect(searchStore),
|
PureMixin,
|
||||||
|
Reflux.connect(searchStore, 'search'),
|
||||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
var state = _.extend({}, searchStore.getState());
|
return {
|
||||||
state.selectedTab = selectedTabStore.getState();
|
search: searchStore.getState(),
|
||||||
|
selectedTab: selectedTabStore.getState()
|
||||||
return state;
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (!prevState.show && this.state.show) {
|
if (!prevState.search.get('show') && this.state.search.get('show')) {
|
||||||
this.refs.input.getDOMNode().focus();
|
this.refs.input.getDOMNode().focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleChange: function(e) {
|
handleChange(e) {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
|
|
||||||
if (tab.channel) {
|
if (tab.channel) {
|
||||||
@ -34,12 +36,12 @@ var Search = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var style = {
|
var style = {
|
||||||
display: this.state.show ? 'block' : 'none'
|
display: this.state.search.get('show') ? 'block' : 'none'
|
||||||
};
|
};
|
||||||
|
|
||||||
var results = _.map(this.state.results, (result) => {
|
var results = this.state.search.get('results').map(result => {
|
||||||
return (
|
return (
|
||||||
<p key={result.id}>{util.timestamp(new Date(result.time * 1000))} {result.from} {result.content}</p>
|
<p key={result.id}>{util.timestamp(new Date(result.time * 1000))} {result.from} {result.content}</p>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var Settings = React.createClass({
|
var Settings = React.createClass({
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Settings</h1>
|
<h1>Settings</h1>
|
||||||
|
@ -15,7 +15,7 @@ var TabList = React.createClass({
|
|||||||
Reflux.connect(privateChatStore, 'privateChats')
|
Reflux.connect(privateChatStore, 'privateChats')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
servers: serverStore.getState(),
|
servers: serverStore.getState(),
|
||||||
channels: channelStore.getState(),
|
channels: channelStore.getState(),
|
||||||
@ -23,15 +23,15 @@ var TabList = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
handleConnectClick: function() {
|
handleConnectClick() {
|
||||||
routeActions.navigate('connect');
|
routeActions.navigate('connect');
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSettingsClick: function() {
|
handleSettingsClick() {
|
||||||
routeActions.navigate('settings');
|
routeActions.navigate('settings');
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var tabs = _.map(this.state.channels, (server, address) => {
|
var tabs = _.map(this.state.channels, (server, address) => {
|
||||||
var serverTabs = _.map(server, (channel, name) => {
|
var serverTabs = _.map(server, (channel, name) => {
|
||||||
return (
|
return (
|
||||||
|
@ -3,27 +3,33 @@ var Reflux = require('reflux');
|
|||||||
|
|
||||||
var selectedTabStore = require('../stores/selectedTab');
|
var selectedTabStore = require('../stores/selectedTab');
|
||||||
var tabActions = require('../actions/tab');
|
var tabActions = require('../actions/tab');
|
||||||
|
var PureMixin = require('../mixins/pure');
|
||||||
|
|
||||||
var TabListItem = React.createClass({
|
var TabListItem = React.createClass({
|
||||||
mixins: [Reflux.connect(selectedTabStore)],
|
mixins: [
|
||||||
|
PureMixin,
|
||||||
|
Reflux.connect(selectedTabStore, 'tab')
|
||||||
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return selectedTabStore.getState();
|
return {
|
||||||
|
tab: selectedTabStore.getState()
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
handleClick: function() {
|
handleClick() {
|
||||||
tabActions.select(this.props.server, this.props.channel);
|
tabActions.select(this.props.server, this.props.channel);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var classes = [];
|
var classes = [];
|
||||||
|
|
||||||
if (!this.props.channel) {
|
if (!this.props.channel) {
|
||||||
classes.push('tab-server');
|
classes.push('tab-server');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.server === this.state.server &&
|
if (this.props.server === this.state.tab.server &&
|
||||||
this.props.channel === this.state.channel) {
|
this.props.channel === this.state.tab.channel) {
|
||||||
classes.push('selected');
|
classes.push('selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ var UserList = React.createClass({
|
|||||||
Reflux.listenTo(selectedTabStore, 'selectedTabChanged')
|
Reflux.listenTo(selectedTabStore, 'selectedTabChanged')
|
||||||
],
|
],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
var tab = selectedTabStore.getState();
|
var tab = selectedTabStore.getState();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -23,32 +23,32 @@ var UserList = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
channelsChanged: function() {
|
channelsChanged() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
|
|
||||||
this.setState({ users: channelStore.getUsers(tab.server, tab.channel) });
|
this.setState({ users: channelStore.getUsers(tab.server, tab.channel) });
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedTabChanged: function(tab) {
|
selectedTabChanged(tab) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedTab: tab,
|
selectedTab: tab,
|
||||||
users: channelStore.getUsers(tab.server, tab.channel)
|
users: channelStore.getUsers(tab.server, tab.channel)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleResize: function() {
|
handleResize() {
|
||||||
this.setState({ height: window.innerHeight - 100 });
|
this.setState({ height: window.innerHeight - 100 });
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
var users = [];
|
var users = [];
|
||||||
var style = {};
|
var style = {};
|
||||||
|
@ -5,14 +5,14 @@ var privateChatActions = require('../actions/privateChat');
|
|||||||
var tabActions = require('../actions/tab');
|
var tabActions = require('../actions/tab');
|
||||||
|
|
||||||
var UserListItem = React.createClass({
|
var UserListItem = React.createClass({
|
||||||
handleClick: function() {
|
handleClick() {
|
||||||
var server = selectedTabStore.getServer();
|
var server = selectedTabStore.getServer();
|
||||||
|
|
||||||
privateChatActions.open(server, this.props.user.nick);
|
privateChatActions.open(server, this.props.user.nick);
|
||||||
tabActions.select(server, this.props.user.nick);
|
tabActions.select(server, this.props.user.nick);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return <p onClick={this.handleClick}>{this.props.user.renderName}</p>;
|
return <p onClick={this.handleClick}>{this.props.user.renderName}</p>;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
17
client/src/js/mixins/pure.js
Normal file
17
client/src/js/mixins/pure.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
var shallowEqual = require('react-pure-render/shallowEqual');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
if (this.context.router) {
|
||||||
|
var changed = this.pureComponentLastPath !== this.context.router.getCurrentPath();
|
||||||
|
this.pureComponentLastPath = this.context.router.getCurrentPath();
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !shallowEqual(this.props, nextProps) ||
|
||||||
|
!shallowEqual(this.state, nextState);
|
||||||
|
}
|
||||||
|
};
|
@ -79,42 +79,42 @@ function sortUsers(server, channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var channelStore = Reflux.createStore({
|
var channelStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
this.listenTo(serverActions.connect, 'addServer');
|
this.listenTo(serverActions.connect, 'addServer');
|
||||||
this.listenTo(serverActions.disconnect, 'removeServer');
|
this.listenTo(serverActions.disconnect, 'removeServer');
|
||||||
this.listenTo(serverActions.load, 'loadServers');
|
this.listenTo(serverActions.load, 'loadServers');
|
||||||
},
|
},
|
||||||
|
|
||||||
part: function(partChannels, server) {
|
part(partChannels, server) {
|
||||||
_.each(partChannels, function(channel) {
|
_.each(partChannels, function(channel) {
|
||||||
delete channels[server][channel];
|
delete channels[server][channel];
|
||||||
});
|
});
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
addUser: function(user, server, channel) {
|
addUser(user, server, channel) {
|
||||||
initChannel(server, channel);
|
initChannel(server, channel);
|
||||||
channels[server][channel].users.push(createUser(user));
|
channels[server][channel].users.push(createUser(user));
|
||||||
sortUsers(server, channel);
|
sortUsers(server, channel);
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeUser: function(user, server, channel) {
|
removeUser(user, server, channel) {
|
||||||
if (channels[server][channel]) {
|
if (channels[server][channel]) {
|
||||||
_.remove(channels[server][channel].users, { nick: user });
|
_.remove(channels[server][channel].users, { nick: user });
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeUserAll: function(user, server) {
|
removeUserAll(user, server) {
|
||||||
_.each(channels[server], function(channel) {
|
_.each(channels[server], function(channel) {
|
||||||
_.remove(channel.users, { nick: user });
|
_.remove(channel.users, { nick: user });
|
||||||
});
|
});
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
renameUser: function(oldNick, newNick, server) {
|
renameUser(oldNick, newNick, server) {
|
||||||
_.each(channels[server], function(channel, channelName) {
|
_.each(channels[server], function(channel, channelName) {
|
||||||
var user = _.find(channel.users, { nick: oldNick });
|
var user = _.find(channel.users, { nick: oldNick });
|
||||||
if (user) {
|
if (user) {
|
||||||
@ -126,7 +126,7 @@ var channelStore = Reflux.createStore({
|
|||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
setUsers: function(users, server, channel) {
|
setUsers(users, server, channel) {
|
||||||
initChannel(server, channel);
|
initChannel(server, channel);
|
||||||
var chan = channels[server][channel];
|
var chan = channels[server][channel];
|
||||||
|
|
||||||
@ -140,12 +140,12 @@ var channelStore = Reflux.createStore({
|
|||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
setTopic: function(topic, server, channel) {
|
setTopic(topic, server, channel) {
|
||||||
channels[server][channel].topic = topic;
|
channels[server][channel].topic = topic;
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
setMode: function(mode) {
|
setMode(mode) {
|
||||||
var user = _.find(channels[mode.server][mode.channel].users, { nick: mode.user });
|
var user = _.find(channels[mode.server][mode.channel].users, { nick: mode.user });
|
||||||
if (user) {
|
if (user) {
|
||||||
_.each(mode.remove, function(mode) {
|
_.each(mode.remove, function(mode) {
|
||||||
@ -161,7 +161,7 @@ var channelStore = Reflux.createStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
load: function(storedChannels) {
|
load(storedChannels) {
|
||||||
_.each(storedChannels, function(channel) {
|
_.each(storedChannels, function(channel) {
|
||||||
initChannel(channel.server, channel.name);
|
initChannel(channel.server, channel.name);
|
||||||
var chan = channels[channel.server][channel.name];
|
var chan = channels[channel.server][channel.name];
|
||||||
@ -179,19 +179,19 @@ var channelStore = Reflux.createStore({
|
|||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
addServer: function(server) {
|
addServer(server) {
|
||||||
if (!(server in channels)) {
|
if (!(server in channels)) {
|
||||||
channels[server] = {};
|
channels[server] = {};
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeServer: function(server) {
|
removeServer(server) {
|
||||||
delete channels[server];
|
delete channels[server];
|
||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadServers: function(storedServers) {
|
loadServers(storedServers) {
|
||||||
_.each(storedServers, function(server) {
|
_.each(storedServers, function(server) {
|
||||||
if (!(server.address in channels)) {
|
if (!(server.address in channels)) {
|
||||||
channels[server.address] = {};
|
channels[server.address] = {};
|
||||||
@ -200,25 +200,25 @@ var channelStore = Reflux.createStore({
|
|||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
getChannels: function(server) {
|
getChannels(server) {
|
||||||
return channels[server];
|
return channels[server];
|
||||||
},
|
},
|
||||||
|
|
||||||
getUsers: function(server, channel) {
|
getUsers(server, channel) {
|
||||||
if (channels[server] && channels[server][channel]) {
|
if (channels[server] && channels[server][channel]) {
|
||||||
return channels[server][channel].users;
|
return channels[server][channel].users;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
|
||||||
getTopic: function(server, channel) {
|
getTopic(server, channel) {
|
||||||
if (channels[server] && channels[server][channel]) {
|
if (channels[server] && channels[server][channel]) {
|
||||||
return channels[server][channel].topic || null;
|
return channels[server][channel].topic || null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -14,11 +14,11 @@ if (stored) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var inputHistoryStore = Reflux.createStore({
|
var inputHistoryStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
},
|
},
|
||||||
|
|
||||||
add: function(line) {
|
add(line) {
|
||||||
if (line.trim() && line !== history[0]) {
|
if (line.trim() && line !== history[0]) {
|
||||||
history.unshift(line);
|
history.unshift(line);
|
||||||
|
|
||||||
@ -30,28 +30,28 @@ var inputHistoryStore = Reflux.createStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset() {
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
index = -1;
|
index = -1;
|
||||||
this.trigger(history[index]);
|
this.trigger(history[index]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
increment: function() {
|
increment() {
|
||||||
if (index !== history.length - 1) {
|
if (index !== history.length - 1) {
|
||||||
index++;
|
index++;
|
||||||
this.trigger(history[index]);
|
this.trigger(history[index]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
decrement: function() {
|
decrement() {
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
index--;
|
index--;
|
||||||
this.trigger(history[index]);
|
this.trigger(history[index]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
return history[index];
|
return history[index];
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
var Reflux = require('reflux');
|
var Reflux = require('reflux');
|
||||||
|
var Immutable = require('immutable');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
var serverStore = require('./server');
|
var serverStore = require('./server');
|
||||||
@ -7,7 +8,18 @@ var actions = require('../actions/message');
|
|||||||
var serverActions = require('../actions/server');
|
var serverActions = require('../actions/server');
|
||||||
var channelActions = require('../actions/channel');
|
var channelActions = require('../actions/channel');
|
||||||
|
|
||||||
var messages = {};
|
var messages = Immutable.Map();
|
||||||
|
var empty = Immutable.List();
|
||||||
|
|
||||||
|
var Message = Immutable.Record({
|
||||||
|
server: null,
|
||||||
|
from: null,
|
||||||
|
to: null,
|
||||||
|
message: '',
|
||||||
|
time: null,
|
||||||
|
type: null,
|
||||||
|
lines: []
|
||||||
|
});
|
||||||
|
|
||||||
function addMessage(message, dest) {
|
function addMessage(message, dest) {
|
||||||
message.time = new Date();
|
message.time = new Date();
|
||||||
@ -19,24 +31,17 @@ function addMessage(message, dest) {
|
|||||||
message.message = from + message.message.slice(7);
|
message.message = from + message.message.slice(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(message.server in messages)) {
|
messages = messages.updateIn([message.server, dest], empty, list => list.push(new Message(message)));
|
||||||
messages[message.server] = {};
|
|
||||||
messages[message.server][dest] = [message];
|
|
||||||
} else if (!(dest in messages[message.server])) {
|
|
||||||
messages[message.server][dest] = [message];
|
|
||||||
} else {
|
|
||||||
messages[message.server][dest].push(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageStore = Reflux.createStore({
|
var messageStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
this.listenTo(serverActions.disconnect, 'disconnect');
|
this.listenTo(serverActions.disconnect, 'disconnect');
|
||||||
this.listenTo(channelActions.part, 'part');
|
this.listenTo(channelActions.part, 'part');
|
||||||
},
|
},
|
||||||
|
|
||||||
send: function(message, to, server) {
|
send(message, to, server) {
|
||||||
addMessage({
|
addMessage({
|
||||||
server: server,
|
server: server,
|
||||||
from: serverStore.getNick(server),
|
from: serverStore.getNick(server),
|
||||||
@ -47,7 +52,7 @@ var messageStore = Reflux.createStore({
|
|||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
add: function(message) {
|
add(message) {
|
||||||
var dest = message.to || message.from;
|
var dest = message.to || message.from;
|
||||||
if (message.from && message.from.indexOf('.') !== -1) {
|
if (message.from && message.from.indexOf('.') !== -1) {
|
||||||
dest = message.server;
|
dest = message.server;
|
||||||
@ -57,7 +62,7 @@ var messageStore = Reflux.createStore({
|
|||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
broadcast: function(message, server, user) {
|
broadcast(message, server, user) {
|
||||||
_.each(channelStore.getChannels(server), function(channel, channelName) {
|
_.each(channelStore.getChannels(server), function(channel, channelName) {
|
||||||
if (!user || (user && _.find(channel.users, { nick: user }))) {
|
if (!user || (user && _.find(channel.users, { nick: user }))) {
|
||||||
addMessage({
|
addMessage({
|
||||||
@ -71,7 +76,7 @@ var messageStore = Reflux.createStore({
|
|||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
inform: function(message, server, channel) {
|
inform(message, server, channel) {
|
||||||
if (_.isArray(message)) {
|
if (_.isArray(message)) {
|
||||||
_.each(message, (msg) => {
|
_.each(message, (msg) => {
|
||||||
addMessage({
|
addMessage({
|
||||||
@ -93,26 +98,23 @@ var messageStore = Reflux.createStore({
|
|||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect: function(server) {
|
disconnect(server) {
|
||||||
delete messages[server];
|
messages = messages.delete(server);
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
part: function(channels, server) {
|
part(channels, server) {
|
||||||
_.each(channels, function(channel) {
|
_.each(channels, function(channel) {
|
||||||
delete messages[server][channel];
|
messages = messages.deleteIn([server, channel]);
|
||||||
});
|
});
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
getMessages: function(server, dest) {
|
getMessages(server, dest) {
|
||||||
if (messages[server] && messages[server][dest]) {
|
return messages.getIn([server, dest]) || empty;
|
||||||
return messages[server][dest];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -10,43 +10,53 @@ var width = window.innerWidth;
|
|||||||
window.charWidth = util.stringWidth(' ', '16px Droid Sans Mono');
|
window.charWidth = util.stringWidth(' ', '16px Droid Sans Mono');
|
||||||
window.messageIndent = 6 * charWidth;
|
window.messageIndent = 6 * charWidth;
|
||||||
|
|
||||||
|
// Temporary hack incase this runs before the font has loaded
|
||||||
|
setTimeout(() => window.charWidth = util.stringWidth(' ', '16px Droid Sans Mono'), 1000);
|
||||||
|
|
||||||
var tab = selectedTabStore.getState();
|
var tab = selectedTabStore.getState();
|
||||||
var messages;
|
var messages;
|
||||||
|
var prev;
|
||||||
|
|
||||||
function wrap() {
|
function wrap() {
|
||||||
messages = messageStore.getMessages(tab.server, tab.channel || tab.server);
|
var next = messageStore.getMessages(tab.server, tab.channel || tab.server);
|
||||||
util.wrapMessages(messages, width, charWidth, messageIndent);
|
if (next !== prev) {
|
||||||
|
prev = next;
|
||||||
|
messages = util.wrapMessages(next, width, charWidth, messageIndent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrap();
|
wrap();
|
||||||
|
|
||||||
var messageLineStore = Reflux.createStore({
|
var messageLineStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenTo(messageActions.setWrapWidth, 'setWrapWidth');
|
this.listenTo(messageActions.setWrapWidth, 'setWrapWidth');
|
||||||
this.listenTo(messageStore, 'messagesChanged');
|
this.listenTo(messageStore, 'messagesChanged');
|
||||||
this.listenTo(selectedTabStore, 'selectedTabChanged');
|
this.listenTo(selectedTabStore, 'selectedTabChanged');
|
||||||
},
|
},
|
||||||
|
|
||||||
setWrapWidth: function(w) {
|
setWrapWidth(w) {
|
||||||
width = w;
|
width = w;
|
||||||
|
messages = util.wrapMessages(messages, width, charWidth, messageIndent);
|
||||||
util.wrapMessages(messages, width, charWidth, messageIndent);
|
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
|
||||||
messagesChanged: function() {
|
messagesChanged() {
|
||||||
wrap();
|
if (wrap()) {
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedTabChanged: function(selectedTab) {
|
selectedTabChanged(selectedTab) {
|
||||||
tab = selectedTab;
|
tab = selectedTab;
|
||||||
|
|
||||||
wrap();
|
if (wrap()) {
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -21,35 +21,35 @@ function initChat(server, nick) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var privateChatStore = Reflux.createStore({
|
var privateChatStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
this.listenTo(messageActions.add, 'messageAdded');
|
this.listenTo(messageActions.add, 'messageAdded');
|
||||||
this.listenTo(serverActions.disconnect, 'disconnect');
|
this.listenTo(serverActions.disconnect, 'disconnect');
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(server, nick) {
|
open(server, nick) {
|
||||||
if (initChat(server, nick)) {
|
if (initChat(server, nick)) {
|
||||||
this.trigger(privateChats);
|
this.trigger(privateChats);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
close: function(server, nick) {
|
close(server, nick) {
|
||||||
delete privateChats[server][nick];
|
delete privateChats[server][nick];
|
||||||
this.trigger(privateChats);
|
this.trigger(privateChats);
|
||||||
},
|
},
|
||||||
|
|
||||||
messageAdded: function(message) {
|
messageAdded(message) {
|
||||||
if (!message.to && message.from.indexOf('.') === -1) {
|
if (!message.to && message.from.indexOf('.') === -1) {
|
||||||
this.open(message.server, message.from);
|
this.open(message.server, message.from);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect: function(server) {
|
disconnect(server) {
|
||||||
delete privateChats[server];
|
delete privateChats[server];
|
||||||
this.trigger(privateChats);
|
this.trigger(privateChats);
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return privateChats;
|
return privateChats;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
var Reflux = require('reflux');
|
var Reflux = require('reflux');
|
||||||
|
var Immutable = require('immutable');
|
||||||
|
|
||||||
var actions = require('../actions/search');
|
var actions = require('../actions/search');
|
||||||
|
|
||||||
var state = {
|
var state = Immutable.Map({
|
||||||
show: false,
|
show: false,
|
||||||
results: []
|
results: Immutable.List()
|
||||||
};
|
});
|
||||||
|
|
||||||
var searchStore = Reflux.createStore({
|
var searchStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
},
|
},
|
||||||
|
|
||||||
searchDone: function(results) {
|
searchDone(results) {
|
||||||
state.results = results;
|
state = state.set('results', Immutable.List(results));
|
||||||
this.trigger(state);
|
this.trigger(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle() {
|
||||||
state.show = !state.show;
|
state = state.update('show', show => !show);
|
||||||
this.trigger(state);
|
this.trigger(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
var Reflux = require('reflux');
|
var Reflux = require('reflux');
|
||||||
|
var Immutable = require('immutable');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
var serverStore = require('./server');
|
var serverStore = require('./server');
|
||||||
@ -8,14 +9,20 @@ var serverActions = require('../actions/server');
|
|||||||
var routeActions = require('../actions/route');
|
var routeActions = require('../actions/route');
|
||||||
var privateChatActions = require('../actions/privateChat');
|
var privateChatActions = require('../actions/privateChat');
|
||||||
|
|
||||||
var selectedTab = {};
|
var Tab = Immutable.Record({
|
||||||
|
server: null,
|
||||||
|
channel: null,
|
||||||
|
name: null
|
||||||
|
});
|
||||||
|
|
||||||
|
var selectedTab = new Tab();
|
||||||
var history = [];
|
var history = [];
|
||||||
|
|
||||||
function selectPrevTab() {
|
function selectPrevTab() {
|
||||||
history.pop();
|
history.pop();
|
||||||
|
|
||||||
if (history.length > 0) {
|
if (history.length > 0) {
|
||||||
selectedTab = _.extend({}, history[history.length - 1]);
|
selectedTab = history[history.length - 1];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,14 +30,12 @@ function selectPrevTab() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateChannelName(name) {
|
function updateChannelName(name) {
|
||||||
selectedTab.channel = name;
|
selectedTab = selectedTab.set('channel', name).set('name', name);
|
||||||
selectedTab.name = name;
|
history[history.length - 1] = selectedTab;
|
||||||
history[history.length - 1].channel = name;
|
|
||||||
history[history.length - 1].name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedTabStore = Reflux.createStore({
|
var selectedTabStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
this.listenTo(channelActions.part, 'part');
|
this.listenTo(channelActions.part, 'part');
|
||||||
this.listenTo(privateChatActions.close, 'close');
|
this.listenTo(privateChatActions.close, 'close');
|
||||||
@ -41,58 +46,57 @@ var selectedTabStore = Reflux.createStore({
|
|||||||
this.listenTo(routeActions.navigate, 'navigate');
|
this.listenTo(routeActions.navigate, 'navigate');
|
||||||
},
|
},
|
||||||
|
|
||||||
select: function(server, channel = null) {
|
select(server, channel = null) {
|
||||||
selectedTab.server = server;
|
selectedTab = new Tab({
|
||||||
selectedTab.channel = channel;
|
server,
|
||||||
|
channel,
|
||||||
|
name: channel || serverStore.getName(server)
|
||||||
|
});
|
||||||
|
|
||||||
if (channel) {
|
history.push(selectedTab);
|
||||||
selectedTab.name = channel;
|
|
||||||
} else {
|
|
||||||
selectedTab.name = serverStore.getName(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
history.push(_.extend({}, selectedTab));
|
|
||||||
|
|
||||||
this.trigger(selectedTab);
|
this.trigger(selectedTab);
|
||||||
},
|
},
|
||||||
|
|
||||||
part: function(channels, server) {
|
part(channels, server) {
|
||||||
if (server === selectedTab.server &&
|
if (server === selectedTab.server &&
|
||||||
channels.indexOf(selectedTab.channel) !== -1) {
|
channels.indexOf(selectedTab.channel) !== -1) {
|
||||||
if (!selectPrevTab()) {
|
if (!selectPrevTab()) {
|
||||||
selectedTab.channel = null;
|
selectedTab = selectedTab
|
||||||
selectedTab.name = serverStore.getName(server);
|
.set('channel', null)
|
||||||
|
.set('name', serverStore.getName(server));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.trigger(selectedTab);
|
this.trigger(selectedTab);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
close: function(server, nick) {
|
close(server, nick) {
|
||||||
if (server === selectedTab.server &&
|
if (server === selectedTab.server &&
|
||||||
nick === selectedTab.channel) {
|
nick === selectedTab.channel) {
|
||||||
if (!selectPrevTab()) {
|
if (!selectPrevTab()) {
|
||||||
selectedTab.channel = null;
|
selectedTab = selectedTab
|
||||||
selectedTab.name = serverStore.getName(server);
|
.set('channel', null)
|
||||||
|
.set('name', serverStore.getName(server));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.trigger(selectedTab);
|
this.trigger(selectedTab);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect: function(server) {
|
disconnect(server) {
|
||||||
if (server === selectedTab.server) {
|
if (server === selectedTab.server) {
|
||||||
_.remove(history, { server: server });
|
_.remove(history, { server: server });
|
||||||
|
|
||||||
if (!selectPrevTab()) {
|
if (!selectPrevTab()) {
|
||||||
selectedTab = {};
|
selectedTab = new Tab();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.trigger(selectedTab);
|
this.trigger(selectedTab);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
userAdded: function(user, server, channel) {
|
userAdded(user, server, channel) {
|
||||||
if (selectedTab.channel &&
|
if (selectedTab.channel &&
|
||||||
server === selectedTab.server &&
|
server === selectedTab.server &&
|
||||||
user === serverStore.getNick(server) &&
|
user === serverStore.getNick(server) &&
|
||||||
@ -103,7 +107,7 @@ var selectedTabStore = Reflux.createStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadChannels: function(channels) {
|
loadChannels(channels) {
|
||||||
_.each(channels, (channel) => {
|
_.each(channels, (channel) => {
|
||||||
if (channel.server === selectedTab.server &&
|
if (channel.server === selectedTab.server &&
|
||||||
channel.name !== selectedTab.channel &&
|
channel.name !== selectedTab.channel &&
|
||||||
@ -118,39 +122,38 @@ var selectedTabStore = Reflux.createStore({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadServers: function(servers) {
|
loadServers(servers) {
|
||||||
var server = _.find(servers, { address: selectedTab.server });
|
var server = _.find(servers, { address: selectedTab.server });
|
||||||
|
|
||||||
if (server && !selectedTab.channel) {
|
if (server && !selectedTab.channel) {
|
||||||
selectedTab.name = server.name;
|
selectedTab = selectedTab.set('name', server.name);
|
||||||
history[history.length - 1].name = server.name;
|
history[history.length - 1] = selectedTab;
|
||||||
|
|
||||||
this.trigger(selectedTab);
|
this.trigger(selectedTab);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
navigate: function(route) {
|
navigate(route) {
|
||||||
if (route.indexOf('.') === -1 && selectedTab.server) {
|
if (route.indexOf('.') === -1 && selectedTab.server) {
|
||||||
selectedTab.server = null;
|
selectedTab = new Tab();
|
||||||
selectedTab.channel = null;
|
|
||||||
this.trigger(selectedTab);
|
this.trigger(selectedTab);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getServer: function() {
|
getServer() {
|
||||||
return selectedTab.server;
|
return selectedTab.server;
|
||||||
},
|
},
|
||||||
|
|
||||||
getChannel: function() {
|
getChannel() {
|
||||||
return selectedTab.channel;
|
return selectedTab.channel;
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return selectedTab;
|
return selectedTab;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
selectedTabStore.listen(function(selectedTab) {
|
selectedTabStore.listen(selectedTab => {
|
||||||
var channel = selectedTab.channel;
|
var channel = selectedTab.channel;
|
||||||
|
|
||||||
if (selectedTab.server) {
|
if (selectedTab.server) {
|
||||||
|
@ -7,11 +7,11 @@ var tabActions = require('../actions/tab');
|
|||||||
var servers = {};
|
var servers = {};
|
||||||
|
|
||||||
var serverStore = Reflux.createStore({
|
var serverStore = Reflux.createStore({
|
||||||
init: function() {
|
init() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
},
|
},
|
||||||
|
|
||||||
connect: function(server, nick, opts) {
|
connect(server, nick, opts) {
|
||||||
var i = server.indexOf(':');
|
var i = server.indexOf(':');
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
server = server.slice(0, i);
|
server = server.slice(0, i);
|
||||||
@ -27,38 +27,38 @@ var serverStore = Reflux.createStore({
|
|||||||
tabActions.select(server);
|
tabActions.select(server);
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect: function(server) {
|
disconnect(server) {
|
||||||
delete servers[server];
|
delete servers[server];
|
||||||
this.trigger(servers);
|
this.trigger(servers);
|
||||||
},
|
},
|
||||||
|
|
||||||
setNick: function(nick, server) {
|
setNick(nick, server) {
|
||||||
servers[server].nick = nick;
|
servers[server].nick = nick;
|
||||||
this.trigger(servers);
|
this.trigger(servers);
|
||||||
},
|
},
|
||||||
|
|
||||||
load: function(storedServers) {
|
load(storedServers) {
|
||||||
_.each(storedServers, function(server) {
|
_.each(storedServers, function(server) {
|
||||||
servers[server.address] = server;
|
servers[server.address] = server;
|
||||||
});
|
});
|
||||||
this.trigger(servers);
|
this.trigger(servers);
|
||||||
},
|
},
|
||||||
|
|
||||||
getNick: function(server) {
|
getNick(server) {
|
||||||
if (servers[server]) {
|
if (servers[server]) {
|
||||||
return servers[server].nick;
|
return servers[server].nick;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
getName: function(server) {
|
getName(server) {
|
||||||
if (servers[server]) {
|
if (servers[server]) {
|
||||||
return servers[server].name;
|
return servers[server].name;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState() {
|
||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -17,67 +17,69 @@ exports.timestamp = function(date) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.wrapMessages = function(messages, width, charWidth, indent = 0) {
|
exports.wrapMessages = function(messages, width, charWidth, indent = 0) {
|
||||||
for (var j = 0, llen = messages.length; j < llen; j++) {
|
return messages.withMutations(m => {
|
||||||
var message = messages[j];
|
for (var j = 0, llen = messages.size; j < llen; j++) {
|
||||||
var lineWidth = (6 + (message.from ? message.from.length + 1 : 0)) * charWidth;
|
var message = messages.get(j);
|
||||||
|
var lineWidth = (6 + (message.from ? message.from.length + 1 : 0)) * charWidth;
|
||||||
|
|
||||||
if (lineWidth + message.message.length * charWidth < width) {
|
if (lineWidth + message.message.length * charWidth < width) {
|
||||||
message.lines = [message.message];
|
m.setIn([j, 'lines'], [message.message]);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
var words = message.message.split(' ');
|
|
||||||
var line = '';
|
|
||||||
var wrapped = [];
|
|
||||||
var wordCount = 0;
|
|
||||||
var hasWrapped = false;
|
|
||||||
|
|
||||||
// Add empty line if first word after timestamp + sender wraps
|
|
||||||
if (words.length > 0 && message.from && lineWidth + words[0].length * charWidth >= width) {
|
|
||||||
wrapped.push(line);
|
|
||||||
lineWidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0, wlen = words.length; i < wlen; i++) {
|
|
||||||
var word = words[i];
|
|
||||||
|
|
||||||
if (hasWrapped) {
|
|
||||||
hasWrapped = false;
|
|
||||||
lineWidth += indent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lineWidth += word.length * charWidth;
|
var words = message.message.split(' ');
|
||||||
wordCount++;
|
var line = '';
|
||||||
|
var wrapped = [];
|
||||||
|
var wordCount = 0;
|
||||||
|
var hasWrapped = false;
|
||||||
|
|
||||||
if (lineWidth >= width) {
|
// Add empty line if first word after timestamp + sender wraps
|
||||||
if (wordCount !== 1) {
|
if (words.length > 0 && message.from && lineWidth + words[0].length * charWidth >= width) {
|
||||||
wrapped.push(line);
|
wrapped.push(line);
|
||||||
|
lineWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (i !== wlen - 1) {
|
for (var i = 0, wlen = words.length; i < wlen; i++) {
|
||||||
line = word + ' ';
|
var word = words[i];
|
||||||
lineWidth = (word.length + 1) * charWidth;
|
|
||||||
wordCount = 1;
|
if (hasWrapped) {
|
||||||
} else {
|
hasWrapped = false;
|
||||||
wrapped.push(word);
|
lineWidth += indent;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wrapped.push(word);
|
|
||||||
lineWidth = 0;
|
|
||||||
wordCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasWrapped = true;
|
lineWidth += word.length * charWidth;
|
||||||
} else if (i !== wlen - 1) {
|
wordCount++;
|
||||||
line += word + ' ';
|
|
||||||
lineWidth += charWidth;
|
|
||||||
} else {
|
|
||||||
line += word;
|
|
||||||
wrapped.push(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message.lines = wrapped;
|
if (lineWidth >= width) {
|
||||||
}
|
if (wordCount !== 1) {
|
||||||
|
wrapped.push(line);
|
||||||
|
|
||||||
|
if (i !== wlen - 1) {
|
||||||
|
line = word + ' ';
|
||||||
|
lineWidth = (word.length + 1) * charWidth;
|
||||||
|
wordCount = 1;
|
||||||
|
} else {
|
||||||
|
wrapped.push(word);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wrapped.push(word);
|
||||||
|
lineWidth = 0;
|
||||||
|
wordCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasWrapped = true;
|
||||||
|
} else if (i !== wlen - 1) {
|
||||||
|
line += word + ' ';
|
||||||
|
lineWidth += charWidth;
|
||||||
|
} else {
|
||||||
|
line += word;
|
||||||
|
wrapped.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.setIn([j, 'lines'], wrapped);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user