Clean up container/component relationship
This commit is contained in:
parent
8b0a53b375
commit
993d29242e
File diff suppressed because one or more lines are too long
@ -270,6 +270,10 @@ i[class^="icon-"]:before, i[class*=" icon-"]:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chat-server .userlist, .chat-private .userlist {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chat-server .userlist-bar, .chat-private .userlist-bar {
|
||||
display: none;
|
||||
}
|
||||
|
24
client/src/js/components/App.js
Normal file
24
client/src/js/components/App.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import Route from '../containers/Route';
|
||||
import Chat from '../containers/Chat';
|
||||
import Connect from '../containers/Connect';
|
||||
import Settings from '../containers/Settings';
|
||||
import TabList from '../components/TabList';
|
||||
|
||||
const App = props => {
|
||||
const { onClick, ...tabListProps } = props;
|
||||
const mainClass = props.showTabList ? 'main-container off-canvas' : 'main-container';
|
||||
|
||||
return (
|
||||
<div onClick={onClick}>
|
||||
<TabList {...tabListProps} />
|
||||
<div className={mainClass}>
|
||||
<Route name="chat"><Chat /></Route>
|
||||
<Route name="connect"><Connect /></Route>
|
||||
<Route name="settings"><Settings /></Route>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -4,30 +4,16 @@ import Navicon from '../components/Navicon';
|
||||
import { linkify } from '../util';
|
||||
|
||||
export default class ChatTitle extends PureComponent {
|
||||
handleLeaveClick = () => {
|
||||
const { tab, disconnect, part, closePrivateChat } = this.props;
|
||||
|
||||
if (tab.isChannel()) {
|
||||
part([tab.name], tab.server);
|
||||
} else if (tab.name) {
|
||||
closePrivateChat(tab.server, tab.name);
|
||||
} else {
|
||||
disconnect(tab.server);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title, tab, channel, toggleSearch, toggleUserList } = this.props;
|
||||
let topic = channel.get('topic');
|
||||
topic = topic ? linkify(topic) : null;
|
||||
const { title, tab, channel, onToggleSearch, onToggleUserList, onCloseClick } = this.props;
|
||||
|
||||
let leaveTitle;
|
||||
let closeTitle;
|
||||
if (tab.isChannel()) {
|
||||
leaveTitle = 'Leave';
|
||||
closeTitle = 'Leave';
|
||||
} else if (tab.name) {
|
||||
leaveTitle = 'Close';
|
||||
closeTitle = 'Close';
|
||||
} else {
|
||||
leaveTitle = 'Disconnect';
|
||||
closeTitle = 'Disconnect';
|
||||
}
|
||||
|
||||
return (
|
||||
@ -36,19 +22,19 @@ export default class ChatTitle extends PureComponent {
|
||||
<Navicon />
|
||||
<span className="chat-title">{title}</span>
|
||||
<div className="chat-topic-wrap">
|
||||
<span className="chat-topic">{topic}</span>
|
||||
<span className="chat-topic">{linkify(channel.get('topic')) || null}</span>
|
||||
</div>
|
||||
<i className="icon-search" title="Search" onClick={toggleSearch} />
|
||||
<i className="icon-search" title="Search" onClick={onToggleSearch} />
|
||||
<i
|
||||
className="icon-cancel button-leave"
|
||||
title={leaveTitle}
|
||||
onClick={this.handleLeaveClick}
|
||||
title={closeTitle}
|
||||
onClick={onCloseClick}
|
||||
/>
|
||||
<i className="icon-user button-userlist" onClick={toggleUserList} />
|
||||
<i className="icon-user button-userlist" onClick={onToggleUserList} />
|
||||
</div>
|
||||
<div className="userlist-bar">
|
||||
<i className="icon-user" />
|
||||
<span className="chat-usercount">{channel.get('users', List()).size || null}</span>
|
||||
<span className="chat-usercount">{channel.get('users', List()).size}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -10,7 +10,6 @@ export default class FileInput extends PureComponent {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
console.log(reader.result.byteLength);
|
||||
this.props.onChange(file.name, reader.result);
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export default class Message extends PureComponent {
|
||||
handleNickClick = () => this.props.onNickClick(this.props.message);
|
||||
handleNickClick = () => this.props.onNickClick(this.props.message.from);
|
||||
|
||||
render() {
|
||||
const { message } = this.props;
|
||||
|
@ -6,14 +6,14 @@ export default class MessageInput extends PureComponent {
|
||||
};
|
||||
|
||||
handleKey = e => {
|
||||
const { tab, runCommand, sendMessage,
|
||||
const { tab, onCommand, onMessage,
|
||||
add, reset, increment, decrement, currentHistoryEntry } = this.props;
|
||||
|
||||
if (e.key === 'Enter' && e.target.value) {
|
||||
if (e.target.value[0] === '/') {
|
||||
runCommand(e.target.value, tab.name, tab.server);
|
||||
onCommand(e.target.value, tab.name, tab.server);
|
||||
} else if (tab.name) {
|
||||
sendMessage(e.target.value, tab.name, tab.server);
|
||||
onMessage(e.target.value, tab.name, tab.server);
|
||||
}
|
||||
|
||||
add(e.target.value);
|
||||
|
@ -1,13 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { toggleMenu } from '../state/ui';
|
||||
import React from 'react';
|
||||
|
||||
class Navicon extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<i className="icon-menu navicon" onClick={this.props.toggleMenu} />
|
||||
);
|
||||
}
|
||||
}
|
||||
const Navicon = ({ toggleMenu }) => (
|
||||
<i className="icon-menu navicon" onClick={toggleMenu} />
|
||||
);
|
||||
|
||||
export default connect(null, { toggleMenu })(Navicon);
|
||||
export default Navicon;
|
||||
|
11
client/src/js/components/Root.js
Normal file
11
client/src/js/components/Root.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import App from '../containers/App';
|
||||
|
||||
const Root = ({ store }) => (
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
export default Root;
|
@ -3,8 +3,8 @@ import TabListItem from './TabListItem';
|
||||
|
||||
export default class TabList extends PureComponent {
|
||||
handleTabClick = (server, target) => this.props.select(server, target);
|
||||
handleConnectClick = () => this.props.pushPath('/connect');
|
||||
handleSettingsClick = () => this.props.pushPath('/settings');
|
||||
handleConnectClick = () => this.props.push('/connect');
|
||||
handleSettingsClick = () => this.props.push('/settings');
|
||||
|
||||
render() {
|
||||
const { tab, channels, servers, privateChats, showTabList } = this.props;
|
||||
|
@ -13,38 +13,31 @@ export default class UserList extends PureComponent {
|
||||
listRef = el => { this.list = el; };
|
||||
|
||||
renderUser = ({ index, style, key }) => {
|
||||
const { users, tab, openPrivateChat, select } = this.props;
|
||||
const { users, onNickClick } = this.props;
|
||||
|
||||
return (
|
||||
<UserListItem
|
||||
key={key}
|
||||
user={users.get(index)}
|
||||
tab={tab}
|
||||
openPrivateChat={openPrivateChat}
|
||||
select={select}
|
||||
style={style}
|
||||
onClick={onNickClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { tab, showUserList } = this.props;
|
||||
const { users, showUserList } = this.props;
|
||||
const className = showUserList ? 'userlist off-canvas' : 'userlist';
|
||||
const style = {};
|
||||
|
||||
if (!tab.isChannel()) {
|
||||
style.display = 'none';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<div className={className}>
|
||||
<AutoSizer disableWidth>
|
||||
{({ height }) => (
|
||||
<List
|
||||
ref={this.listRef}
|
||||
width={200}
|
||||
height={height - 20}
|
||||
rowCount={this.props.users.size}
|
||||
rowCount={users.size}
|
||||
rowHeight={24}
|
||||
rowRenderer={this.renderUser}
|
||||
className="rvlist-users"
|
||||
|
@ -1,12 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export default class UserListItem extends PureComponent {
|
||||
handleClick = () => {
|
||||
const { tab, user, openPrivateChat, select } = this.props;
|
||||
|
||||
openPrivateChat(tab.server, user.nick);
|
||||
select(tab.server, user.nick, true);
|
||||
};
|
||||
handleClick = () => this.props.onClick(this.props.user.nick);
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
101
client/src/js/components/pages/Chat.js
Normal file
101
client/src/js/components/pages/Chat.js
Normal file
@ -0,0 +1,101 @@
|
||||
import React, { Component } from 'react';
|
||||
import ChatTitle from '../ChatTitle';
|
||||
import Search from '../Search';
|
||||
import MessageBox from '../MessageBox';
|
||||
import MessageInput from '../MessageInput';
|
||||
import UserList from '../UserList';
|
||||
|
||||
export default class Chat extends Component {
|
||||
handleCloseClick = () => {
|
||||
const { tab, part, closePrivateChat, disconnect } = this.props;
|
||||
|
||||
if (tab.isChannel()) {
|
||||
part([tab.name], tab.server);
|
||||
} else if (tab.name) {
|
||||
closePrivateChat(tab.server, tab.name);
|
||||
} else {
|
||||
disconnect(tab.server);
|
||||
}
|
||||
};
|
||||
|
||||
handleSearch = phrase => {
|
||||
const { tab, searchMessages } = this.props;
|
||||
if (tab.isChannel()) {
|
||||
searchMessages(tab.server, tab.name, phrase);
|
||||
}
|
||||
};
|
||||
|
||||
handleNickClick = nick => {
|
||||
const { tab, openPrivateChat, select } = this.props;
|
||||
openPrivateChat(tab.server, nick);
|
||||
select(tab.server, nick);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
channel,
|
||||
currentInputHistoryEntry,
|
||||
hasMoreMessages,
|
||||
messages,
|
||||
nick,
|
||||
search,
|
||||
showUserList,
|
||||
tab,
|
||||
title,
|
||||
users,
|
||||
|
||||
fetchMessages,
|
||||
inputActions,
|
||||
runCommand,
|
||||
sendMessage,
|
||||
toggleSearch,
|
||||
toggleUserList
|
||||
} = this.props;
|
||||
|
||||
let chatClass;
|
||||
if (tab.isChannel()) {
|
||||
chatClass = 'chat-channel';
|
||||
} else if (tab.name) {
|
||||
chatClass = 'chat-private';
|
||||
} else {
|
||||
chatClass = 'chat-server';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={chatClass}>
|
||||
<ChatTitle
|
||||
channel={channel}
|
||||
tab={tab}
|
||||
title={title}
|
||||
onCloseClick={this.handleCloseClick}
|
||||
onToggleSearch={toggleSearch}
|
||||
onToggleUserList={toggleUserList}
|
||||
/>
|
||||
<Search
|
||||
search={search}
|
||||
onSearch={this.handleSearch}
|
||||
/>
|
||||
<MessageBox
|
||||
hasMoreMessages={hasMoreMessages}
|
||||
messages={messages}
|
||||
tab={tab}
|
||||
onFetchMore={fetchMessages}
|
||||
onNickClick={this.handleNickClick}
|
||||
/>
|
||||
<MessageInput
|
||||
currentHistoryEntry={currentInputHistoryEntry}
|
||||
nick={nick}
|
||||
tab={tab}
|
||||
onCommand={runCommand}
|
||||
onMessage={sendMessage}
|
||||
{...inputActions}
|
||||
/>
|
||||
<UserList
|
||||
showUserList={showUserList}
|
||||
users={users}
|
||||
onNickClick={this.handleNickClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
100
client/src/js/components/pages/Connect.js
Normal file
100
client/src/js/components/pages/Connect.js
Normal file
@ -0,0 +1,100 @@
|
||||
import React, { Component } from 'react';
|
||||
import Navicon from '../../containers/Navicon';
|
||||
|
||||
export default class Connect extends Component {
|
||||
state = {
|
||||
showOptionals: false,
|
||||
passwordTouched: false
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
const { connect, select, join } = this.props;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
let address = e.target.address.value.trim();
|
||||
const nick = e.target.nick.value.trim();
|
||||
const channels = e.target.channels.value.split(',').map(s => s.trim()).filter(s => s);
|
||||
const opts = {
|
||||
name: e.target.name.value.trim(),
|
||||
tls: e.target.ssl.checked
|
||||
};
|
||||
|
||||
if (this.state.showOptionals) {
|
||||
opts.realname = e.target.realname.value.trim();
|
||||
opts.username = e.target.username.value.trim();
|
||||
|
||||
if (this.state.passwordTouched) {
|
||||
opts.password = e.target.password.value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (address.indexOf('.') > 0 && nick) {
|
||||
connect(address, nick, opts);
|
||||
|
||||
const i = address.indexOf(':');
|
||||
if (i > 0) {
|
||||
address = address.slice(0, i);
|
||||
}
|
||||
|
||||
select(address);
|
||||
|
||||
if (channels.length > 0) {
|
||||
join(channels, address);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleShowClick = () => {
|
||||
this.setState({ showOptionals: !this.state.showOptionals });
|
||||
};
|
||||
|
||||
handlePasswordChange = () => {
|
||||
this.setState({ passwordTouched: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { defaults } = this.props;
|
||||
let optionals = null;
|
||||
|
||||
if (this.state.showOptionals) {
|
||||
optionals = (
|
||||
<div>
|
||||
<input name="username" type="text" placeholder="Username" />
|
||||
<input
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
defaultValue={defaults.password ? ' ' : null}
|
||||
onChange={this.handlePasswordChange}
|
||||
/>
|
||||
<input name="realname" type="text" placeholder="Realname" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="connect">
|
||||
<Navicon />
|
||||
<form className="connect-form" onSubmit={this.handleSubmit}>
|
||||
<h1>Connect</h1>
|
||||
<input name="name" type="text" placeholder="Name" defaultValue={defaults.name} />
|
||||
<input name="address" type="text" placeholder="Address" defaultValue={defaults.address} />
|
||||
<input name="nick" type="text" placeholder="Nick" />
|
||||
<input
|
||||
name="channels"
|
||||
type="text"
|
||||
placeholder="Channels"
|
||||
defaultValue={defaults.channels ? defaults.channels.join(',') : null}
|
||||
/>
|
||||
{optionals}
|
||||
<p>
|
||||
<label htmlFor="ssl"><input name="ssl" type="checkbox" defaultChecked={defaults.ssl} />SSL</label>
|
||||
<i className="icon-ellipsis" onClick={this.handleShowClick} />
|
||||
</p>
|
||||
<input type="submit" value="Connect" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
34
client/src/js/components/pages/Settings.js
Normal file
34
client/src/js/components/pages/Settings.js
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import Navicon from '../../containers/Navicon';
|
||||
import FileInput from '../FileInput';
|
||||
|
||||
const Settings = ({ settings, onCertChange, onKeyChange, uploadCert }) => {
|
||||
const status = settings.get('uploadingCert') ? 'Uploading...' : 'Upload';
|
||||
const error = settings.get('certError');
|
||||
|
||||
return (
|
||||
<div className="settings">
|
||||
<Navicon />
|
||||
<h1>Settings</h1>
|
||||
<h2>Client Certificate</h2>
|
||||
<div>
|
||||
<p>Certificate</p>
|
||||
<FileInput
|
||||
name={settings.get('certFile') || 'Select Certificate'}
|
||||
onChange={onCertChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p>Private Key</p>
|
||||
<FileInput
|
||||
name={settings.get('keyFile') || 'Select Key'}
|
||||
onChange={onKeyChange}
|
||||
/>
|
||||
</div>
|
||||
<button onClick={uploadCert}>{status}</button>
|
||||
{ error ? <p className="error">{error}</p> : null }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
@ -1,41 +1,13 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import { push } from '../util/router';
|
||||
import Route from './Route';
|
||||
import Chat from './Chat';
|
||||
import Connect from './Connect';
|
||||
import Settings from './Settings';
|
||||
import TabList from '../components/TabList';
|
||||
import App from '../components/App';
|
||||
import { getChannels } from '../state/channels';
|
||||
import { getPrivateChats } from '../state/privateChats';
|
||||
import { getServers } from '../state/servers';
|
||||
import { getSelectedTab, select } from '../state/tab';
|
||||
import { getShowTabList, hideMenu } from '../state/ui';
|
||||
|
||||
class App extends PureComponent {
|
||||
handleClick = () => {
|
||||
if (this.props.showTabList) {
|
||||
this.props.hideMenu();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { showTabList } = this.props;
|
||||
const mainClass = showTabList ? 'main-container off-canvas' : 'main-container';
|
||||
|
||||
return (
|
||||
<div onClick={this.handleClick}>
|
||||
<TabList {...this.props} />
|
||||
<div className={mainClass}>
|
||||
<Route name="chat"><Chat /></Route>
|
||||
<Route name="connect"><Connect /></Route>
|
||||
<Route name="settings"><Settings /></Route>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
import { push } from '../util/router';
|
||||
|
||||
const mapState = createStructuredSelector({
|
||||
channels: getChannels,
|
||||
@ -45,4 +17,13 @@ const mapState = createStructuredSelector({
|
||||
tab: getSelectedTab
|
||||
});
|
||||
|
||||
export default connect(mapState, { pushPath: push, select, hideMenu })(App);
|
||||
const mapDispatch = (dispatch, props) => ({
|
||||
onClick: () => {
|
||||
if (props.showTabList) {
|
||||
dispatch(hideMenu());
|
||||
}
|
||||
},
|
||||
...bindActionCreators({ push, select }, dispatch)
|
||||
});
|
||||
|
||||
export default connect(mapState, mapDispatch)(App);
|
||||
|
@ -1,12 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import ChatTitle from '../components/ChatTitle';
|
||||
import Search from '../components/Search';
|
||||
import MessageBox from '../components/MessageBox';
|
||||
import MessageInput from '../components/MessageInput';
|
||||
import UserList from '../components/UserList';
|
||||
import Chat from '../components/pages/Chat';
|
||||
import { getSelectedTabTitle } from '../state';
|
||||
import { getSelectedChannel, getSelectedChannelUsers, part } from '../state/channels';
|
||||
import { getCurrentInputHistoryEntry, addInputHistory, resetInputHistory,
|
||||
@ -19,80 +14,6 @@ import { getCurrentNick, disconnect } from '../state/servers';
|
||||
import { getSelectedTab, select } from '../state/tab';
|
||||
import { getShowUserList, toggleUserList } from '../state/ui';
|
||||
|
||||
class Chat extends PureComponent {
|
||||
handleSearch = phrase => {
|
||||
const { dispatch, tab } = this.props;
|
||||
if (tab.isChannel()) {
|
||||
dispatch(searchMessages(tab.server, tab.name, phrase));
|
||||
}
|
||||
};
|
||||
|
||||
handleMessageNickClick = message => {
|
||||
const { tab } = this.props;
|
||||
|
||||
this.props.openPrivateChat(tab.server, message.from);
|
||||
this.props.select(tab.server, message.from);
|
||||
};
|
||||
|
||||
handleFetchMore = () => this.props.dispatch(fetchMessages());
|
||||
|
||||
render() {
|
||||
const { title, tab, channel, search, currentInputHistoryEntry,
|
||||
messages, hasMoreMessages, users, showUserList, nick, inputActions } = this.props;
|
||||
|
||||
let chatClass;
|
||||
if (tab.isChannel()) {
|
||||
chatClass = 'chat-channel';
|
||||
} else if (tab.name) {
|
||||
chatClass = 'chat-private';
|
||||
} else {
|
||||
chatClass = 'chat-server';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={chatClass}>
|
||||
<ChatTitle
|
||||
title={title}
|
||||
tab={tab}
|
||||
channel={channel}
|
||||
toggleSearch={this.props.toggleSearch}
|
||||
toggleUserList={this.props.toggleUserList}
|
||||
disconnect={this.props.disconnect}
|
||||
part={this.props.part}
|
||||
closePrivateChat={this.props.closePrivateChat}
|
||||
/>
|
||||
<Search
|
||||
search={search}
|
||||
onSearch={this.handleSearch}
|
||||
/>
|
||||
<MessageBox
|
||||
messages={messages}
|
||||
hasMoreMessages={hasMoreMessages}
|
||||
tab={tab}
|
||||
onNickClick={this.handleMessageNickClick}
|
||||
onFetchMore={this.handleFetchMore}
|
||||
/>
|
||||
<MessageInput
|
||||
tab={tab}
|
||||
channel={channel}
|
||||
currentHistoryEntry={currentInputHistoryEntry}
|
||||
nick={nick}
|
||||
runCommand={this.props.runCommand}
|
||||
sendMessage={this.props.sendMessage}
|
||||
{...inputActions}
|
||||
/>
|
||||
<UserList
|
||||
users={users}
|
||||
tab={tab}
|
||||
showUserList={showUserList}
|
||||
select={this.props.select}
|
||||
openPrivateChat={this.props.openPrivateChat}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapState = createStructuredSelector({
|
||||
channel: getSelectedChannel,
|
||||
currentInputHistoryEntry: getCurrentInputHistoryEntry,
|
||||
@ -106,28 +27,27 @@ const mapState = createStructuredSelector({
|
||||
users: getSelectedChannelUsers
|
||||
});
|
||||
|
||||
function mapDispatch(dispatch) {
|
||||
return {
|
||||
dispatch,
|
||||
...bindActionCreators({
|
||||
closePrivateChat,
|
||||
disconnect,
|
||||
openPrivateChat,
|
||||
part,
|
||||
runCommand,
|
||||
searchMessages,
|
||||
select,
|
||||
sendMessage,
|
||||
toggleSearch,
|
||||
toggleUserList
|
||||
}, dispatch),
|
||||
inputActions: bindActionCreators({
|
||||
add: addInputHistory,
|
||||
reset: resetInputHistory,
|
||||
increment: incrementInputHistory,
|
||||
decrement: decrementInputHistory
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
const mapDispatch = dispatch => ({
|
||||
...bindActionCreators({
|
||||
closePrivateChat,
|
||||
disconnect,
|
||||
fetchMessages,
|
||||
openPrivateChat,
|
||||
part,
|
||||
runCommand,
|
||||
searchMessages,
|
||||
select,
|
||||
sendMessage,
|
||||
toggleSearch,
|
||||
toggleUserList
|
||||
}, dispatch),
|
||||
|
||||
inputActions: bindActionCreators({
|
||||
add: addInputHistory,
|
||||
reset: resetInputHistory,
|
||||
increment: incrementInputHistory,
|
||||
decrement: decrementInputHistory
|
||||
}, dispatch)
|
||||
});
|
||||
|
||||
export default connect(mapState, mapDispatch)(Chat);
|
||||
|
@ -1,111 +1,19 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import Navicon from '../components/Navicon';
|
||||
import Connect from '../components/pages/Connect';
|
||||
import { join } from '../state/channels';
|
||||
import { getConnectDefaults } from '../state/environment';
|
||||
import { connect as connectServer } from '../state/servers';
|
||||
import { select } from '../state/tab';
|
||||
|
||||
class Connect extends PureComponent {
|
||||
state = {
|
||||
showOptionals: false,
|
||||
passwordTouched: false
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { dispatch } = this.props;
|
||||
let address = e.target.address.value.trim();
|
||||
const nick = e.target.nick.value.trim();
|
||||
const channels = e.target.channels.value.split(',').map(s => s.trim()).filter(s => s);
|
||||
const opts = {
|
||||
name: e.target.name.value.trim(),
|
||||
tls: e.target.ssl.checked
|
||||
};
|
||||
|
||||
if (this.state.showOptionals) {
|
||||
opts.realname = e.target.realname.value.trim();
|
||||
opts.username = e.target.username.value.trim();
|
||||
|
||||
if (this.state.passwordTouched) {
|
||||
opts.password = e.target.password.value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (address.indexOf('.') > 0 && nick) {
|
||||
dispatch(connectServer(address, nick, opts));
|
||||
|
||||
const i = address.indexOf(':');
|
||||
if (i > 0) {
|
||||
address = address.slice(0, i);
|
||||
}
|
||||
|
||||
dispatch(select(address));
|
||||
|
||||
if (channels.length > 0) {
|
||||
dispatch(join(channels, address));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleShowClick = () => {
|
||||
this.setState({ showOptionals: !this.state.showOptionals });
|
||||
};
|
||||
|
||||
handlePasswordChange = () => {
|
||||
this.setState({ passwordTouched: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { defaults } = this.props;
|
||||
let optionals = null;
|
||||
|
||||
if (this.state.showOptionals) {
|
||||
optionals = (
|
||||
<div>
|
||||
<input name="username" type="text" placeholder="Username" />
|
||||
<input
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
defaultValue={defaults.password ? ' ' : null}
|
||||
onChange={this.handlePasswordChange}
|
||||
/>
|
||||
<input name="realname" type="text" placeholder="Realname" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="connect">
|
||||
<Navicon />
|
||||
<form className="connect-form" onSubmit={this.handleSubmit}>
|
||||
<h1>Connect</h1>
|
||||
<input name="name" type="text" placeholder="Name" defaultValue={defaults.name} />
|
||||
<input name="address" type="text" placeholder="Address" defaultValue={defaults.address} />
|
||||
<input name="nick" type="text" placeholder="Nick" />
|
||||
<input
|
||||
name="channels"
|
||||
type="text"
|
||||
placeholder="Channels"
|
||||
defaultValue={defaults.channels ? defaults.channels.join(',') : null}
|
||||
/>
|
||||
{optionals}
|
||||
<p>
|
||||
<label htmlFor="ssl"><input name="ssl" type="checkbox" defaultChecked={defaults.ssl} />SSL</label>
|
||||
<i className="icon-ellipsis" onClick={this.handleShowClick} />
|
||||
</p>
|
||||
<input type="submit" value="Connect" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapState = createStructuredSelector({
|
||||
defaults: getConnectDefaults
|
||||
});
|
||||
|
||||
export default connect(mapState)(Connect);
|
||||
const mapDispatch = {
|
||||
join,
|
||||
connect: connectServer,
|
||||
select
|
||||
};
|
||||
|
||||
export default connect(mapState, mapDispatch)(Connect);
|
||||
|
7
client/src/js/containers/Navicon.js
Normal file
7
client/src/js/containers/Navicon.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import Navicon from '../components/Navicon';
|
||||
import { toggleMenu } from '../state/ui';
|
||||
|
||||
const mapDispatch = { toggleMenu };
|
||||
|
||||
export default connect(null, mapDispatch)(Navicon);
|
@ -1,14 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import App from './App';
|
||||
|
||||
export default class Root extends Component {
|
||||
render() {
|
||||
const { store } = this.props;
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,48 +1,17 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import Navicon from '../components/Navicon';
|
||||
import FileInput from '../components/FileInput';
|
||||
import Settings from '../components/pages/Settings';
|
||||
import { getSettings, setCert, setKey, uploadCert } from '../state/settings';
|
||||
|
||||
class Settings extends PureComponent {
|
||||
handleCertChange = (name, data) => this.props.dispatch(setCert(name, data));
|
||||
handleKeyChange = (name, data) => this.props.dispatch(setKey(name, data));
|
||||
handleCertUpload = () => this.props.dispatch(uploadCert());
|
||||
|
||||
render() {
|
||||
const { settings } = this.props;
|
||||
const status = settings.get('uploadingCert') ? 'Uploading...' : 'Upload';
|
||||
const error = settings.get('certError');
|
||||
|
||||
return (
|
||||
<div className="settings">
|
||||
<Navicon />
|
||||
<h1>Settings</h1>
|
||||
<h2>Client Certificate</h2>
|
||||
<div>
|
||||
<p>Certificate</p>
|
||||
<FileInput
|
||||
name={settings.get('certFile') || 'Select Certificate'}
|
||||
onChange={this.handleCertChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p>Private Key</p>
|
||||
<FileInput
|
||||
name={settings.get('keyFile') || 'Select Key'}
|
||||
onChange={this.handleKeyChange}
|
||||
/>
|
||||
</div>
|
||||
<button onClick={this.handleCertUpload}>{status}</button>
|
||||
{ error ? <p className="error">{error}</p> : null }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapState = createStructuredSelector({
|
||||
settings: getSettings
|
||||
});
|
||||
|
||||
export default connect(mapState)(Settings);
|
||||
const mapDispatch = dispatch => ({
|
||||
onCertChange(name, data) { dispatch(setCert(name, data)); },
|
||||
onKeyChange(name, data) { dispatch(setKey(name, data)); },
|
||||
...bindActionCreators({ uploadCert }, dispatch)
|
||||
});
|
||||
|
||||
export default connect(mapState, mapDispatch)(Settings);
|
||||
|
@ -7,7 +7,7 @@ import configureStore from './store';
|
||||
import initRouter from './util/router';
|
||||
import routes from './routes';
|
||||
import Socket from './util/Socket';
|
||||
import Root from './containers/Root';
|
||||
import Root from './components/Root';
|
||||
import runModules from './modules';
|
||||
|
||||
const host = DEV ? `${window.location.hostname}:1337` : window.location.host;
|
||||
@ -27,5 +27,5 @@ const renderRoot = () => render(
|
||||
renderRoot();
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./containers/Root', () => renderRoot());
|
||||
module.hot.accept('./components/Root', () => renderRoot());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user