commit
c76797e68e
|
@ -11,6 +11,7 @@ from the final changelog of the release.
|
||||||
Release date: TBD
|
Release date: TBD
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
- Added a new team button next to the team tabs
|
||||||
|
|
||||||
#### All Platforms
|
#### All Platforms
|
||||||
- Suppress white screen which is displayed for a moment on startup
|
- Suppress white screen which is displayed for a moment on startup
|
||||||
|
|
|
@ -10,6 +10,8 @@ const MattermostView = require('./MattermostView.jsx');
|
||||||
const TabBar = require('./TabBar.jsx');
|
const TabBar = require('./TabBar.jsx');
|
||||||
const HoveringURL = require('./HoveringURL.jsx');
|
const HoveringURL = require('./HoveringURL.jsx');
|
||||||
|
|
||||||
|
const NewTeamModal = require('./NewTeamModal.jsx');
|
||||||
|
|
||||||
// Todo: Need to consider better way to apply styles
|
// Todo: Need to consider better way to apply styles
|
||||||
const styles = {
|
const styles = {
|
||||||
hoveringURL: {
|
hoveringURL: {
|
||||||
|
@ -36,7 +38,8 @@ const MainPage = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
disablewebsecurity: React.PropTypes.bool.isRequired,
|
disablewebsecurity: React.PropTypes.bool.isRequired,
|
||||||
onUnreadCountChange: React.PropTypes.func.isRequired,
|
onUnreadCountChange: React.PropTypes.func.isRequired,
|
||||||
teams: React.PropTypes.array.isRequired
|
teams: React.PropTypes.array.isRequired,
|
||||||
|
onTeamConfigChange: React.PropTypes.func.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
|
@ -127,6 +130,10 @@ const MainPage = React.createClass({
|
||||||
mattermost.goForward();
|
mattermost.goForward();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('add-server', () => {
|
||||||
|
this.addServer();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
|
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
|
||||||
|
@ -237,6 +244,11 @@ const MainPage = React.createClass({
|
||||||
this.setState({targetURL});
|
this.setState({targetURL});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
addServer() {
|
||||||
|
this.setState({
|
||||||
|
showNewTeamModal: true
|
||||||
|
});
|
||||||
|
},
|
||||||
render() {
|
render() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -253,6 +265,7 @@ const MainPage = React.createClass({
|
||||||
mentionAtActiveCounts={this.state.mentionAtActiveCounts}
|
mentionAtActiveCounts={this.state.mentionAtActiveCounts}
|
||||||
activeKey={this.state.key}
|
activeKey={this.state.key}
|
||||||
onSelect={this.handleSelect}
|
onSelect={this.handleSelect}
|
||||||
|
onAddServer={this.addServer}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
@ -296,6 +309,25 @@ const MainPage = React.createClass({
|
||||||
authServerURL = `${tmpURL.protocol}//${tmpURL.host}`;
|
authServerURL = `${tmpURL.protocol}//${tmpURL.host}`;
|
||||||
authInfo = this.state.loginQueue[0].authInfo;
|
authInfo = this.state.loginQueue[0].authInfo;
|
||||||
}
|
}
|
||||||
|
var modal = (
|
||||||
|
<NewTeamModal
|
||||||
|
show={this.state.showNewTeamModal}
|
||||||
|
onClose={() => {
|
||||||
|
this.setState({
|
||||||
|
showNewTeamModal: false
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onSave={(newTeam) => {
|
||||||
|
this.props.teams.push(newTeam);
|
||||||
|
this.setState({
|
||||||
|
showNewTeamModal: false,
|
||||||
|
key: this.props.teams.length - 1
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
this.props.onTeamConfigChange(this.props.teams);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LoginModal
|
<LoginModal
|
||||||
|
@ -323,6 +355,9 @@ const MainPage = React.createClass({
|
||||||
targetURL={this.state.targetURL}
|
targetURL={this.state.targetURL}
|
||||||
/> }
|
/> }
|
||||||
</ReactCSSTransitionGroup>
|
</ReactCSSTransitionGroup>
|
||||||
|
<div>
|
||||||
|
{ modal }
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
209
src/browser/components/NewTeamModal.jsx
Normal file
209
src/browser/components/NewTeamModal.jsx
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
const React = require('react');
|
||||||
|
const {Modal, Button, FormGroup, FormControl, ControlLabel, HelpBlock} = require('react-bootstrap');
|
||||||
|
|
||||||
|
class NewTeamModal extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.wasShown = false;
|
||||||
|
this.state = {
|
||||||
|
teamName: '',
|
||||||
|
teamUrl: '',
|
||||||
|
saveStarted: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.initializeOnShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeOnShow() {
|
||||||
|
this.state = {
|
||||||
|
teamName: this.props.team ? this.props.team.name : '',
|
||||||
|
teamUrl: this.props.team ? this.props.team.url : '',
|
||||||
|
teamIndex: this.props.team ? this.props.team.index : false,
|
||||||
|
saveStarted: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTeamNameValidationError() {
|
||||||
|
if (!this.state.saveStarted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.state.teamName.length > 0 ? null : 'Name is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
getTeamNameValidationState() {
|
||||||
|
return this.getTeamNameValidationError() === null ? null : 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTeamNameChange(e) {
|
||||||
|
this.setState({
|
||||||
|
teamName: e.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTeamUrlValidationError() {
|
||||||
|
if (!this.state.saveStarted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (this.state.teamUrl.length === 0) {
|
||||||
|
return 'URL is required.';
|
||||||
|
}
|
||||||
|
if (!(/^https?:\/\/.*/).test(this.state.teamUrl.trim())) {
|
||||||
|
return 'URL should start with http:// or https://.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTeamUrlValidationState() {
|
||||||
|
return this.getTeamUrlValidationError() === null ? null : 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTeamUrlChange(e) {
|
||||||
|
this.setState({
|
||||||
|
teamUrl: e.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getError() {
|
||||||
|
return this.getTeamNameValidationError() || this.getTeamUrlValidationError();
|
||||||
|
}
|
||||||
|
|
||||||
|
validateForm() {
|
||||||
|
return this.getTeamNameValidationState() === null &&
|
||||||
|
this.getTeamUrlValidationState() === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.setState({
|
||||||
|
saveStarted: true
|
||||||
|
}, () => {
|
||||||
|
if (this.validateForm()) {
|
||||||
|
this.props.onSave({
|
||||||
|
url: this.state.teamUrl,
|
||||||
|
name: this.state.teamName,
|
||||||
|
index: this.state.teamIndex
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getSaveButtonLabel() {
|
||||||
|
if (this.props.editMode) {
|
||||||
|
return 'Save';
|
||||||
|
}
|
||||||
|
return 'Add';
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle() {
|
||||||
|
if (this.props.editMode) {
|
||||||
|
return 'Edit Server';
|
||||||
|
}
|
||||||
|
return 'Add Server';
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const noBottomSpaceing = {
|
||||||
|
'padding-bottom': 0,
|
||||||
|
'margin-bottom': 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.wasShown !== this.props.show && this.props.show) {
|
||||||
|
this.initializeOnShow();
|
||||||
|
}
|
||||||
|
this.wasShown = this.props.show;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
show={this.props.show}
|
||||||
|
id='newServerModal'
|
||||||
|
onHide={this.props.onClose}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'Enter':
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
// The add button from behind this might still be focused
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
break;
|
||||||
|
case 'Escape':
|
||||||
|
this.props.onClose();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Modal.Header>
|
||||||
|
<Modal.Title>{this.getModalTitle()}</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
|
||||||
|
<Modal.Body>
|
||||||
|
<form>
|
||||||
|
<FormGroup
|
||||||
|
validationState={this.getTeamNameValidationState()}
|
||||||
|
>
|
||||||
|
<ControlLabel>{'Server Display Name'}</ControlLabel>
|
||||||
|
<FormControl
|
||||||
|
id='teamNameInput'
|
||||||
|
type='text'
|
||||||
|
value={this.state.teamName}
|
||||||
|
placeholder='Server Name'
|
||||||
|
onChange={this.handleTeamNameChange.bind(this)}
|
||||||
|
/>
|
||||||
|
<FormControl.Feedback/>
|
||||||
|
<HelpBlock>{'The name of the server displayed on your desktop app tab bar.'}</HelpBlock>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
validationState={this.getTeamUrlValidationState()}
|
||||||
|
style={noBottomSpaceing}
|
||||||
|
>
|
||||||
|
<ControlLabel>{'Server URL'}</ControlLabel>
|
||||||
|
<FormControl
|
||||||
|
id='teamUrlInput'
|
||||||
|
type='text'
|
||||||
|
value={this.state.teamUrl}
|
||||||
|
placeholder='https://example.com'
|
||||||
|
onChange={this.handleTeamUrlChange.bind(this)}
|
||||||
|
/>
|
||||||
|
<FormControl.Feedback/>
|
||||||
|
<HelpBlock
|
||||||
|
style={noBottomSpaceing}
|
||||||
|
>{'The URL of your Mattermost server. Must start with http:// or https://.'}</HelpBlock>
|
||||||
|
</FormGroup>
|
||||||
|
</form>
|
||||||
|
</Modal.Body>
|
||||||
|
|
||||||
|
<Modal.Footer>
|
||||||
|
<div
|
||||||
|
className='pull-left modal-error'
|
||||||
|
>
|
||||||
|
{this.getError()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
id='cancelNewServerModal'
|
||||||
|
onClick={this.props.onClose}
|
||||||
|
>{'Cancel'}</Button>
|
||||||
|
<Button
|
||||||
|
id='saveNewServerModal'
|
||||||
|
onClick={this.save.bind(this)}
|
||||||
|
disabled={!this.validateForm()}
|
||||||
|
bsStyle='primary'
|
||||||
|
>{this.getSaveButtonLabel()}</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewTeamModal.propTypes = {
|
||||||
|
onClose: React.PropTypes.func,
|
||||||
|
onSave: React.PropTypes.func,
|
||||||
|
team: React.PropTypes.object,
|
||||||
|
editMode: React.PropTypes.boolean,
|
||||||
|
show: React.PropTypes.boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = NewTeamModal;
|
|
@ -46,6 +46,11 @@ const SettingsPage = React.createClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
ipcRenderer.on('add-server', () => {
|
||||||
|
this.setState({
|
||||||
|
showAddTeamForm: true
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
handleTeamsChange(teams) {
|
handleTeamsChange(teams) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -141,6 +146,11 @@ const SettingsPage = React.createClass({
|
||||||
showAddTeamForm: !this.state.showAddTeamForm
|
showAddTeamForm: !this.state.showAddTeamForm
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setShowTeamFormVisibility(val) {
|
||||||
|
this.setState({
|
||||||
|
showAddTeamForm: val
|
||||||
|
});
|
||||||
|
},
|
||||||
handleFlashWindow() {
|
handleFlashWindow() {
|
||||||
this.setState({
|
this.setState({
|
||||||
notifications: {
|
notifications: {
|
||||||
|
@ -153,6 +163,23 @@ const SettingsPage = React.createClass({
|
||||||
showUnreadBadge: !this.refs.showUnreadBadge.props.checked
|
showUnreadBadge: !this.refs.showUnreadBadge.props.checked
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateTeam(index, newData) {
|
||||||
|
var teams = this.state.teams;
|
||||||
|
teams[index] = newData;
|
||||||
|
this.setState({
|
||||||
|
teams
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addServer(team) {
|
||||||
|
var teams = this.state.teams;
|
||||||
|
teams.push(team);
|
||||||
|
this.setState({
|
||||||
|
teams
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var teamsRow = (
|
var teamsRow = (
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -160,7 +187,11 @@ const SettingsPage = React.createClass({
|
||||||
<TeamList
|
<TeamList
|
||||||
teams={this.state.teams}
|
teams={this.state.teams}
|
||||||
showAddTeamForm={this.state.showAddTeamForm}
|
showAddTeamForm={this.state.showAddTeamForm}
|
||||||
|
toggleAddTeamForm={this.toggleShowTeamForm}
|
||||||
|
setAddTeamFormVisibility={this.setShowTeamFormVisibility}
|
||||||
onTeamsChange={this.handleTeamsChange}
|
onTeamsChange={this.handleTeamsChange}
|
||||||
|
updateTeam={this.updateTeam}
|
||||||
|
addServer={this.addServer}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -354,6 +385,7 @@ const SettingsPage = React.createClass({
|
||||||
<p className='text-right'>
|
<p className='text-right'>
|
||||||
<a
|
<a
|
||||||
style={settingsPage.sectionHeadingLink}
|
style={settingsPage.sectionHeadingLink}
|
||||||
|
id='addNewServer'
|
||||||
href='#'
|
href='#'
|
||||||
onClick={this.toggleShowTeamForm}
|
onClick={this.toggleShowTeamForm}
|
||||||
>{'⊞ Add new server'}</a>
|
>{'⊞ Add new server'}</a>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const {Nav, NavItem} = require('react-bootstrap');
|
const {Nav, NavItem, Button} = require('react-bootstrap');
|
||||||
|
|
||||||
class TabBar extends React.Component {
|
class TabBar extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
@ -75,16 +75,30 @@ class TabBar extends React.Component {
|
||||||
onSelect={this.props.onSelect}
|
onSelect={this.props.onSelect}
|
||||||
>
|
>
|
||||||
{ tabs }
|
{ tabs }
|
||||||
|
{ this.renderAddTeamButton() }
|
||||||
</Nav>
|
</Nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderAddTeamButton() {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
id='tabBarAddNewTeam'
|
||||||
|
onClick={this.props.onAddServer}
|
||||||
|
bsStyle='tabButton'
|
||||||
|
>
|
||||||
|
{'+'}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar.propTypes = {
|
TabBar.propTypes = {
|
||||||
activeKey: React.PropTypes.number,
|
activeKey: React.PropTypes.number,
|
||||||
id: React.PropTypes.string,
|
id: React.PropTypes.string,
|
||||||
onSelect: React.PropTypes.func,
|
onSelect: React.PropTypes.func,
|
||||||
teams: React.PropTypes.array
|
teams: React.PropTypes.array,
|
||||||
|
onAddServer: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = TabBar;
|
module.exports = TabBar;
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const {ListGroup} = require('react-bootstrap');
|
const {ListGroup} = require('react-bootstrap');
|
||||||
const TeamListItem = require('./TeamListItem.jsx');
|
const TeamListItem = require('./TeamListItem.jsx');
|
||||||
const TeamListItemNew = require('./TeamListItemNew.jsx');
|
const NewTeamModal = require('./NewTeamModal.jsx');
|
||||||
const RemoveServerModal = require('./RemoveServerModal.jsx');
|
const RemoveServerModal = require('./RemoveServerModal.jsx');
|
||||||
|
|
||||||
const TeamList = React.createClass({
|
const TeamList = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onTeamsChange: React.PropTypes.func,
|
onTeamsChange: React.PropTypes.func,
|
||||||
showAddTeamForm: React.PropTypes.bool,
|
showAddTeamForm: React.PropTypes.bool,
|
||||||
teams: React.PropTypes.array
|
teams: React.PropTypes.array,
|
||||||
|
addServer: React.PropTypes.func,
|
||||||
|
updateTeam: React.PropTypes.func,
|
||||||
|
toggleAddTeamForm: React.PropTypes.func,
|
||||||
|
setAddTeamFormVisibility: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
showTeamListItemNew: false,
|
showEditTeamForm: false,
|
||||||
indexToRemoveServer: -1,
|
indexToRemoveServer: -1,
|
||||||
team: {
|
team: {
|
||||||
url: '',
|
url: '',
|
||||||
|
@ -40,7 +44,7 @@ const TeamList = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showTeamListItemNew: false,
|
showEditTeamForm: false,
|
||||||
team: {
|
team: {
|
||||||
url: '',
|
url: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -52,7 +56,7 @@ const TeamList = React.createClass({
|
||||||
},
|
},
|
||||||
handleTeamEditing(teamName, teamUrl, teamIndex) {
|
handleTeamEditing(teamName, teamUrl, teamIndex) {
|
||||||
this.setState({
|
this.setState({
|
||||||
showTeamListItemNew: true,
|
showEditTeamForm: true,
|
||||||
team: {
|
team: {
|
||||||
url: teamUrl,
|
url: teamUrl,
|
||||||
name: teamName,
|
name: teamName,
|
||||||
|
@ -92,19 +96,45 @@ const TeamList = React.createClass({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
var addTeamForm;
|
var addServerForm = (
|
||||||
if (this.props.showAddTeamForm || this.state.showTeamListItemNew) {
|
<NewTeamModal
|
||||||
addTeamForm = (
|
show={this.props.showAddTeamForm || this.state.showEditTeamForm}
|
||||||
<TeamListItemNew
|
editMode={this.state.showEditTeamForm}
|
||||||
key={this.state.team.index}
|
onClose={() => {
|
||||||
onTeamAdd={this.handleTeamAdd}
|
this.setState({
|
||||||
teamIndex={this.state.team.index}
|
showEditTeamForm: false,
|
||||||
teamName={this.state.team.name}
|
team: {
|
||||||
teamUrl={this.state.team.url}
|
name: '',
|
||||||
/>);
|
url: '',
|
||||||
} else {
|
index: false
|
||||||
addTeamForm = '';
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
this.props.setAddTeamFormVisibility(false);
|
||||||
|
}}
|
||||||
|
onSave={(newTeam) => {
|
||||||
|
var teamData = {
|
||||||
|
name: newTeam.name,
|
||||||
|
url: newTeam.url
|
||||||
|
};
|
||||||
|
if (this.props.showAddTeamForm) {
|
||||||
|
this.props.addServer(teamData);
|
||||||
|
} else {
|
||||||
|
this.props.updateTeam(newTeam.index, teamData);
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
showNewTeamModal: false,
|
||||||
|
showEditTeamForm: false,
|
||||||
|
team: {
|
||||||
|
name: '',
|
||||||
|
url: '',
|
||||||
|
index: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
this.props.setAddTeamFormVisibility(false);
|
||||||
|
}}
|
||||||
|
team={this.state.team}
|
||||||
|
/>);
|
||||||
|
|
||||||
const removeServer = this.props.teams[this.state.indexToRemoveServer];
|
const removeServer = this.props.teams[this.state.indexToRemoveServer];
|
||||||
const removeServerModal = (
|
const removeServerModal = (
|
||||||
|
@ -123,7 +153,7 @@ const TeamList = React.createClass({
|
||||||
return (
|
return (
|
||||||
<ListGroup className='teamList'>
|
<ListGroup className='teamList'>
|
||||||
{ teamNodes }
|
{ teamNodes }
|
||||||
{ addTeamForm }
|
{ addServerForm }
|
||||||
{ removeServerModal}
|
{ removeServerModal}
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
);
|
);
|
||||||
|
|
22
src/browser/config/AppConfig.js
Normal file
22
src/browser/config/AppConfig.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
const settings = require('../../common/settings');
|
||||||
|
const {remote} = require('electron');
|
||||||
|
|
||||||
|
class AppConfig {
|
||||||
|
constructor(file) {
|
||||||
|
this.fileName = file;
|
||||||
|
try {
|
||||||
|
this.data = settings.readFileSync(file);
|
||||||
|
} catch (e) {
|
||||||
|
this.data = {
|
||||||
|
teams: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
this.data[key] = value;
|
||||||
|
settings.writeFileSync(this.fileName, this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new AppConfig(remote.app.getPath('userData') + '/config.json');
|
|
@ -16,3 +16,25 @@
|
||||||
opacity: 0.01;
|
opacity: 0.01;
|
||||||
transition: opacity 500ms ease-in-out;
|
transition: opacity 500ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-tabButton {
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #333;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tabButton:hover {
|
||||||
|
color: #333;
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
border-color: #adadad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-error .control-label,
|
||||||
|
.has-error .help-block {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-error {
|
||||||
|
color: #a94442;
|
||||||
|
}
|
||||||
|
|
|
@ -9,19 +9,12 @@ const ReactDOM = require('react-dom');
|
||||||
const {remote, ipcRenderer} = require('electron');
|
const {remote, ipcRenderer} = require('electron');
|
||||||
const MainPage = require('./components/MainPage.jsx');
|
const MainPage = require('./components/MainPage.jsx');
|
||||||
|
|
||||||
const settings = require('../common/settings');
|
const AppConfig = require('./config/AppConfig.js');
|
||||||
const badge = require('./js/badge');
|
const badge = require('./js/badge');
|
||||||
|
|
||||||
remote.getCurrentWindow().removeAllListeners('focus');
|
remote.getCurrentWindow().removeAllListeners('focus');
|
||||||
|
|
||||||
var config;
|
if (AppConfig.data.teams.length === 0) {
|
||||||
try {
|
|
||||||
const configFile = remote.app.getPath('userData') + '/config.json';
|
|
||||||
config = settings.readFileSync(configFile);
|
|
||||||
} catch (e) {
|
|
||||||
window.location = 'settings.html';
|
|
||||||
}
|
|
||||||
if (config.teams.length === 0) {
|
|
||||||
window.location = 'settings.html';
|
window.location = 'settings.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +33,7 @@ function showUnreadBadgeWindows(unreadCount, mentionCount) {
|
||||||
if (mentionCount > 0) {
|
if (mentionCount > 0) {
|
||||||
const dataURL = badge.createDataURL(mentionCount.toString());
|
const dataURL = badge.createDataURL(mentionCount.toString());
|
||||||
sendBadge(dataURL, 'You have unread mentions (' + mentionCount + ')');
|
sendBadge(dataURL, 'You have unread mentions (' + mentionCount + ')');
|
||||||
} else if (unreadCount > 0 && config.showUnreadBadge) {
|
} else if (unreadCount > 0 && AppConfig.data.showUnreadBadge) {
|
||||||
const dataURL = badge.createDataURL('•');
|
const dataURL = badge.createDataURL('•');
|
||||||
sendBadge(dataURL, 'You have unread channels (' + unreadCount + ')');
|
sendBadge(dataURL, 'You have unread channels (' + unreadCount + ')');
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,7 +44,7 @@ function showUnreadBadgeWindows(unreadCount, mentionCount) {
|
||||||
function showUnreadBadgeOSX(unreadCount, mentionCount) {
|
function showUnreadBadgeOSX(unreadCount, mentionCount) {
|
||||||
if (mentionCount > 0) {
|
if (mentionCount > 0) {
|
||||||
remote.app.dock.setBadge(mentionCount.toString());
|
remote.app.dock.setBadge(mentionCount.toString());
|
||||||
} else if (unreadCount > 0 && config.showUnreadBadge) {
|
} else if (unreadCount > 0 && AppConfig.data.showUnreadBadge) {
|
||||||
remote.app.dock.setBadge('•');
|
remote.app.dock.setBadge('•');
|
||||||
} else {
|
} else {
|
||||||
remote.app.dock.setBadge('');
|
remote.app.dock.setBadge('');
|
||||||
|
@ -89,11 +82,16 @@ function showUnreadBadge(unreadCount, mentionCount) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function teamConfigChange(teams) {
|
||||||
|
AppConfig.set('teams', teams);
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<MainPage
|
<MainPage
|
||||||
disablewebsecurity={config.disablewebsecurity}
|
disablewebsecurity={AppConfig.data.disablewebsecurity}
|
||||||
teams={config.teams}
|
teams={AppConfig.data.teams}
|
||||||
onUnreadCountChange={showUnreadBadge}
|
onUnreadCountChange={showUnreadBadge}
|
||||||
|
onTeamConfigChange={teamConfigChange}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById('content')
|
document.getElementById('content')
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Settings</title>
|
<title>Settings</title>
|
||||||
<link rel="stylesheet" href="modules/bootstrap/css/bootstrap.min.css">
|
<link rel="stylesheet" href="modules/bootstrap/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="css/index.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -27,6 +27,11 @@ function createTemplate(mainWindow, config) {
|
||||||
click() {
|
click() {
|
||||||
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Sign in to Another Server',
|
||||||
|
click() {
|
||||||
|
mainWindow.webContents.send('add-server');
|
||||||
|
}
|
||||||
}, separatorItem, {
|
}, separatorItem, {
|
||||||
role: 'hide'
|
role: 'hide'
|
||||||
}, {
|
}, {
|
||||||
|
@ -41,6 +46,11 @@ function createTemplate(mainWindow, config) {
|
||||||
click() {
|
click() {
|
||||||
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Sign in to Another Server',
|
||||||
|
click() {
|
||||||
|
mainWindow.webContents.send('add-server');
|
||||||
|
}
|
||||||
}, separatorItem, {
|
}, separatorItem, {
|
||||||
role: 'quit',
|
role: 'quit',
|
||||||
accelerator: 'CmdOrCtrl+Q',
|
accelerator: 'CmdOrCtrl+Q',
|
||||||
|
|
|
@ -171,4 +171,11 @@ describe('browser/index.html', function desc() {
|
||||||
browserWindow.getTitle().should.eventually.equal('Title 1');
|
browserWindow.getTitle().should.eventually.equal('Title 1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should open the new server prompt after clicking the add button', () => {
|
||||||
|
// See settings_test for specs that cover the actual prompt
|
||||||
|
return this.app.client.waitUntilWindowLoaded().
|
||||||
|
click('#tabBarAddNewTeam').
|
||||||
|
isExisting('#newServerModal').should.eventually.be.true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -245,4 +245,107 @@ describe('browser/settings.html', function desc() {
|
||||||
isExisting(modalTitleSelector).should.eventually.false;
|
isExisting(modalTitleSelector).should.eventually.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('NewTeamModal', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
env.addClientCommands(this.app.client);
|
||||||
|
return this.app.client.
|
||||||
|
loadSettingsPage().
|
||||||
|
click('#addNewServer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open the new server modal', () => {
|
||||||
|
return this.app.client.isExisting('#newServerModal').should.eventually.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the window after clicking cancel', () => {
|
||||||
|
return this.app.client.
|
||||||
|
click('#cancelNewServerModal').
|
||||||
|
pause(1000). // Animation
|
||||||
|
isExisting('#newServerModal').should.eventually.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be valid if no team name has been set', () => {
|
||||||
|
return this.app.client.
|
||||||
|
click('#saveNewServerModal').
|
||||||
|
isExisting('.has-error #teamNameInput').should.eventually.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be valid if no server address has been set', () => {
|
||||||
|
return this.app.client.
|
||||||
|
click('#saveNewServerModal').
|
||||||
|
isExisting('.has-error #teamUrlInput').should.eventually.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Valid server name', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return this.app.client.
|
||||||
|
setValue('#teamNameInput', 'TestTeam').
|
||||||
|
click('#saveNewServerModal');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be marked invalid', () => {
|
||||||
|
return this.app.client.
|
||||||
|
isExisting('.has-error #teamNameInput').should.eventually.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be possible to click save', () => {
|
||||||
|
return this.app.client.
|
||||||
|
getAttribute('#saveNewServerModal', 'disabled').should.eventually.equal('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Valid server url', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return this.app.client.
|
||||||
|
setValue('#teamUrlInput', 'http://example.org').
|
||||||
|
click('#saveNewServerModal');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be valid', () => {
|
||||||
|
return this.app.client.
|
||||||
|
isExisting('.has-error #teamUrlInput').should.eventually.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be possible to click save', () => {
|
||||||
|
return this.app.client.
|
||||||
|
getAttribute('#saveNewServerModal', 'disabled').should.eventually.equal('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be valid if an invalid server address has been set', () => {
|
||||||
|
return this.app.client.
|
||||||
|
setValue('#teamUrlInput', 'superInvalid url').
|
||||||
|
click('#saveNewServerModal').
|
||||||
|
isExisting('.has-error #teamUrlInput').should.eventually.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Valid Team Settings', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return this.app.client.
|
||||||
|
setValue('#teamUrlInput', 'http://example.org').
|
||||||
|
setValue('#teamNameInput', 'TestTeam');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be possible to click add', () => {
|
||||||
|
return this.app.client.
|
||||||
|
getAttribute('#saveNewServerModal', 'disabled').should.eventually.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add the team to the config file', (done) => {
|
||||||
|
this.app.client.
|
||||||
|
click('#saveNewServerModal').
|
||||||
|
pause(1000). // Animation
|
||||||
|
click('#btnSave').
|
||||||
|
pause(1000).then(() => {
|
||||||
|
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
|
||||||
|
savedConfig.teams.should.contain({
|
||||||
|
name: 'TestTeam',
|
||||||
|
url: 'http://example.org'
|
||||||
|
});
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue