Immutable messages, search and selectedTab
This commit is contained in:
parent
77c723344c
commit
11ea241b60
26 changed files with 332 additions and 260 deletions
|
@ -13,7 +13,7 @@ var App = React.createClass({
|
|||
Reflux.listenTo(routeActions.navigate, 'navigate')
|
||||
],
|
||||
|
||||
navigate: function(path, replace) {
|
||||
navigate(path, replace) {
|
||||
if (!replace) {
|
||||
this.transitionTo(path);
|
||||
} else {
|
||||
|
@ -21,7 +21,7 @@ var App = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<TabList />
|
||||
|
|
|
@ -9,20 +9,22 @@ var MessageInput = require('./MessageInput.jsx');
|
|||
var UserList = require('./UserList.jsx');
|
||||
var selectedTabStore = require('../stores/selectedTab');
|
||||
var tabActions = require('../actions/tab');
|
||||
var PureMixin = require('../mixins/pure');
|
||||
|
||||
var Chat = React.createClass({
|
||||
mixins: [
|
||||
PureMixin,
|
||||
Router.State,
|
||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
return {
|
||||
selectedTab: selectedTabStore.getState()
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
componentWillMount() {
|
||||
if (!window.loaded) {
|
||||
var p = this.getParams();
|
||||
|
||||
|
@ -34,7 +36,7 @@ var Chat = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var chatClass;
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ var ChatTitle = React.createClass({
|
|||
Reflux.listenTo(selectedTabStore, 'selectedTabChanged')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
var tab = selectedTabStore.getState();
|
||||
|
||||
return {
|
||||
|
@ -23,20 +23,20 @@ var ChatTitle = React.createClass({
|
|||
};
|
||||
},
|
||||
|
||||
channelsChanged: function() {
|
||||
channelsChanged() {
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
this.setState({ usercount: channelStore.getUsers(tab.server, tab.channel).length });
|
||||
},
|
||||
|
||||
selectedTabChanged: function(tab) {
|
||||
selectedTabChanged(tab) {
|
||||
this.setState({
|
||||
selectedTab: tab,
|
||||
usercount: channelStore.getUsers(tab.server, tab.channel).length
|
||||
});
|
||||
},
|
||||
|
||||
handleLeaveClick: function() {
|
||||
handleLeaveClick() {
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
if (!tab.channel) {
|
||||
|
@ -48,7 +48,7 @@ var ChatTitle = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var tab = this.state.selectedTab;
|
||||
var leaveTitle;
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ var serverActions = require('../actions/server');
|
|||
var channelActions = require('../actions/channel');
|
||||
|
||||
var Connect = React.createClass({
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
return {
|
||||
showOptionals: false
|
||||
};
|
||||
},
|
||||
|
||||
handleSubmit: function(e) {
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var address = e.target.address.value.trim();
|
||||
|
|
|
@ -5,17 +5,20 @@ var Infinite = require('react-infinite');
|
|||
var Autolinker = require('autolinker');
|
||||
|
||||
var MessageHeader = require('./MessageHeader.jsx');
|
||||
var MessageLine = require('./MessageLine.jsx');
|
||||
var messageLineStore = require('../stores/messageLine');
|
||||
var selectedTabStore = require('../stores/selectedTab');
|
||||
var messageActions = require('../actions/message');
|
||||
var PureMixin = require('../mixins/pure');
|
||||
|
||||
var MessageBox = React.createClass({
|
||||
mixins: [
|
||||
PureMixin,
|
||||
Reflux.connect(messageLineStore, 'messages'),
|
||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
return {
|
||||
messages: messageLineStore.getState(),
|
||||
selectedTab: selectedTabStore.getState(),
|
||||
|
@ -23,20 +26,20 @@ var MessageBox = React.createClass({
|
|||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
},
|
||||
|
||||
componentWillUpdate: function() {
|
||||
componentWillUpdate() {
|
||||
var el = this.refs.list.getDOMNode();
|
||||
this.autoScroll = el.scrollTop + el.offsetHeight === el.scrollHeight;
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
componentDidUpdate() {
|
||||
this.updateWidth();
|
||||
|
||||
if (this.autoScroll) {
|
||||
|
@ -45,12 +48,12 @@ var MessageBox = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
handleResize: function() {
|
||||
handleResize() {
|
||||
this.updateWidth();
|
||||
this.setState({ height: window.innerHeight - 100 });
|
||||
},
|
||||
|
||||
updateWidth: function() {
|
||||
updateWidth() {
|
||||
var width = this.refs.list.getDOMNode().firstChild.offsetWidth;
|
||||
|
||||
if (this.width !== width) {
|
||||
|
@ -59,7 +62,7 @@ var MessageBox = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var tab = this.state.selectedTab;
|
||||
var dest = tab.channel || tab.server;
|
||||
var lines = [];
|
||||
|
@ -67,27 +70,17 @@ var MessageBox = React.createClass({
|
|||
paddingLeft: this.props.indent + 'px'
|
||||
};
|
||||
|
||||
for (var j = 0; j < this.state.messages.length; j++) {
|
||||
var message = this.state.messages[j];
|
||||
var messageClass = 'message';
|
||||
var key = message.server + dest + j;
|
||||
|
||||
if (message.type) {
|
||||
messageClass += ' message-' + message.type;
|
||||
}
|
||||
this.state.messages.forEach((message, j) => {
|
||||
var key = message.server + dest + j;
|
||||
|
||||
lines.push(<MessageHeader key={key} message={message} />);
|
||||
|
||||
for (var i = 1; i < message.lines.length; i++) {
|
||||
var line = Autolinker.link(message.lines[i], { keepOriginalText: true });
|
||||
|
||||
lines.push(
|
||||
<p key={key + '-' + i} className={messageClass} style={innerStyle}>
|
||||
<span dangerouslySetInnerHTML={{ __html: line }}></span>
|
||||
</p>
|
||||
<MessageLine key={key + '-' + i} type={message.type} line={message.lines[i]} />
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (lines.length !== 1) {
|
||||
return (
|
||||
|
|
|
@ -7,14 +7,18 @@ var privateChatActions = require('../actions/privateChat');
|
|||
var tabActions = require('../actions/tab');
|
||||
|
||||
var MessageHeader = React.createClass({
|
||||
handleSenderClick: function() {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.message.lines[0] !== this.props.message.lines[0];
|
||||
},
|
||||
|
||||
handleSenderClick() {
|
||||
var message = this.props.message;
|
||||
|
||||
privateChatActions.open(message.server, message.from);
|
||||
tabActions.select(message.server, message.from);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var message = this.props.message;
|
||||
var sender = null;
|
||||
var messageClass = 'message';
|
||||
|
|
|
@ -6,33 +6,33 @@ var selectedTabStore = require('../stores/selectedTab');
|
|||
var messageActions = require('../actions/message');
|
||||
var inputHistoryActions = require('../actions/inputHistory');
|
||||
var tabActions = require('../actions/tab');
|
||||
var PureMixin = require('../mixins/pure');
|
||||
|
||||
var MessageInput = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(selectedTabStore, 'selectedTab'),
|
||||
PureMixin,
|
||||
Reflux.connect(inputHistoryStore, 'history'),
|
||||
Reflux.listenTo(tabActions.select, 'tabSelected')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
return {
|
||||
selectedTab: selectedTabStore.getState(),
|
||||
history: inputHistoryStore.getState(),
|
||||
value: ''
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this.refs.input.getDOMNode().focus();
|
||||
},
|
||||
|
||||
tabSelected: function() {
|
||||
tabSelected() {
|
||||
this.refs.input.getDOMNode().focus();
|
||||
},
|
||||
|
||||
handleKey: function(e) {
|
||||
handleKey(e) {
|
||||
if (e.which === 13 && e.target.value) {
|
||||
var tab = this.state.selectedTab;
|
||||
var tab = selectedTabStore.getState();
|
||||
|
||||
if (e.target.value[0] === '/') {
|
||||
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 });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
return (
|
||||
<div className="message-input-wrap">
|
||||
<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 selectedTabStore = require('../stores/selectedTab');
|
||||
var searchActions = require('../actions/search');
|
||||
var PureMixin = require('../mixins/pure');
|
||||
|
||||
var Search = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(searchStore),
|
||||
PureMixin,
|
||||
Reflux.connect(searchStore, 'search'),
|
||||
Reflux.connect(selectedTabStore, 'selectedTab')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
var state = _.extend({}, searchStore.getState());
|
||||
state.selectedTab = selectedTabStore.getState();
|
||||
|
||||
return state;
|
||||
getInitialState() {
|
||||
return {
|
||||
search: searchStore.getState(),
|
||||
selectedTab: selectedTabStore.getState()
|
||||
};
|
||||
},
|
||||
|
||||
componentDidUpdate: function(prevProps, prevState) {
|
||||
if (!prevState.show && this.state.show) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!prevState.search.get('show') && this.state.search.get('show')) {
|
||||
this.refs.input.getDOMNode().focus();
|
||||
}
|
||||
},
|
||||
|
||||
handleChange: function(e) {
|
||||
handleChange(e) {
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
if (tab.channel) {
|
||||
|
@ -34,12 +36,12 @@ var Search = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
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 (
|
||||
<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 Settings = React.createClass({
|
||||
render: function() {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Settings</h1>
|
||||
|
|
|
@ -15,7 +15,7 @@ var TabList = React.createClass({
|
|||
Reflux.connect(privateChatStore, 'privateChats')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
return {
|
||||
servers: serverStore.getState(),
|
||||
channels: channelStore.getState(),
|
||||
|
@ -23,15 +23,15 @@ var TabList = React.createClass({
|
|||
};
|
||||
},
|
||||
|
||||
handleConnectClick: function() {
|
||||
handleConnectClick() {
|
||||
routeActions.navigate('connect');
|
||||
},
|
||||
|
||||
handleSettingsClick: function() {
|
||||
handleSettingsClick() {
|
||||
routeActions.navigate('settings');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var tabs = _.map(this.state.channels, (server, address) => {
|
||||
var serverTabs = _.map(server, (channel, name) => {
|
||||
return (
|
||||
|
|
|
@ -3,27 +3,33 @@ var Reflux = require('reflux');
|
|||
|
||||
var selectedTabStore = require('../stores/selectedTab');
|
||||
var tabActions = require('../actions/tab');
|
||||
var PureMixin = require('../mixins/pure');
|
||||
|
||||
var TabListItem = React.createClass({
|
||||
mixins: [Reflux.connect(selectedTabStore)],
|
||||
mixins: [
|
||||
PureMixin,
|
||||
Reflux.connect(selectedTabStore, 'tab')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
return selectedTabStore.getState();
|
||||
getInitialState() {
|
||||
return {
|
||||
tab: selectedTabStore.getState()
|
||||
};
|
||||
},
|
||||
|
||||
handleClick: function() {
|
||||
handleClick() {
|
||||
tabActions.select(this.props.server, this.props.channel);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var classes = [];
|
||||
|
||||
if (!this.props.channel) {
|
||||
classes.push('tab-server');
|
||||
}
|
||||
|
||||
if (this.props.server === this.state.server &&
|
||||
this.props.channel === this.state.channel) {
|
||||
if (this.props.server === this.state.tab.server &&
|
||||
this.props.channel === this.state.tab.channel) {
|
||||
classes.push('selected');
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ var UserList = React.createClass({
|
|||
Reflux.listenTo(selectedTabStore, 'selectedTabChanged')
|
||||
],
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState() {
|
||||
var tab = selectedTabStore.getState();
|
||||
|
||||
return {
|
||||
|
@ -23,32 +23,32 @@ var UserList = React.createClass({
|
|||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
},
|
||||
|
||||
channelsChanged: function() {
|
||||
channelsChanged() {
|
||||
var tab = this.state.selectedTab;
|
||||
|
||||
this.setState({ users: channelStore.getUsers(tab.server, tab.channel) });
|
||||
},
|
||||
|
||||
selectedTabChanged: function(tab) {
|
||||
selectedTabChanged(tab) {
|
||||
this.setState({
|
||||
selectedTab: tab,
|
||||
users: channelStore.getUsers(tab.server, tab.channel)
|
||||
});
|
||||
},
|
||||
|
||||
handleResize: function() {
|
||||
handleResize() {
|
||||
this.setState({ height: window.innerHeight - 100 });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var tab = this.state.selectedTab;
|
||||
var users = [];
|
||||
var style = {};
|
||||
|
|
|
@ -5,14 +5,14 @@ var privateChatActions = require('../actions/privateChat');
|
|||
var tabActions = require('../actions/tab');
|
||||
|
||||
var UserListItem = React.createClass({
|
||||
handleClick: function() {
|
||||
handleClick() {
|
||||
var server = selectedTabStore.getServer();
|
||||
|
||||
privateChatActions.open(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>;
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue