mattermost-desktop/src/browser/settings.jsx

527 lines
17 KiB
React
Raw Normal View History

'use strict';
window.eval = global.eval = function() {
throw new Error("Sorry, Mattermost does not support window.eval() for security reasons.");
}
2016-05-21 00:13:22 -07:00
const {remote, ipcRenderer} = require('electron');
2015-12-22 06:35:22 -08:00
const settings = require('../common/settings');
2016-01-30 07:50:43 -08:00
const React = require('react');
const ReactDOM = require('react-dom');
2016-09-11 08:18:09 -07:00
const {Grid, Row, Col, Input, Button, ListGroup, ListGroupItem, Glyphicon, HelpBlock, Navbar, Nav} = require('react-bootstrap');
var AutoLaunch = require('auto-launch');
2016-01-30 07:50:43 -08:00
2015-12-21 07:58:41 -08:00
var appLauncher = new AutoLaunch({
2016-08-02 12:29:24 -07:00
name: 'Mattermost',
isHidden: true
});
2016-02-11 08:12:28 -08:00
function backToIndex() {
remote.getCurrentWindow().loadURL('file://' + __dirname + '/index.html');
2016-01-30 07:50:43 -08:00
}
var SettingsPage = React.createClass({
getInitialState: function() {
var initialState;
try {
initialState = settings.readFileSync(this.props.configFile);
} catch (e) {
initialState = settings.loadDefault();
}
2016-05-25 11:31:57 -07:00
initialState.showAddTeamForm = false;
initialState.trayWasVisible = remote.getCurrentWindow().trayWasVisible;
2016-05-25 11:31:57 -07:00
return initialState;
},
componentDidMount: function() {
if (process.platform === 'win32' || process.platform === 'linux') {
var self = this;
appLauncher.isEnabled().then(function(enabled) {
self.setState({
autostart: enabled
});
});
}
},
handleTeamsChange: function(teams) {
2015-12-19 07:39:51 -08:00
this.setState({
showAddTeamForm: false,
2015-12-19 07:39:51 -08:00
teams: teams
});
},
handleSave: function() {
var config = {
teams: this.state.teams,
hideMenuBar: this.state.hideMenuBar,
2016-04-11 05:51:24 -07:00
showTrayIcon: this.state.showTrayIcon,
trayIconTheme: this.state.trayIconTheme,
2016-04-17 07:07:17 -07:00
disablewebsecurity: this.state.disablewebsecurity,
version: settings.version,
minimizeToTray: this.state.minimizeToTray,
toggleWindowOnTrayIconClick: this.state.toggleWindowOnTrayIconClick,
notifications: {
flashWindow: this.state.notifications.flashWindow
2016-07-15 04:04:14 -07:00
},
showUnreadBadge: this.state.showUnreadBadge
};
settings.writeFileSync(this.props.configFile, config);
2016-01-23 04:33:50 -08:00
if (process.platform === 'win32' || process.platform === 'linux') {
var currentWindow = remote.getCurrentWindow();
currentWindow.setAutoHideMenuBar(config.hideMenuBar);
currentWindow.setMenuBarVisibility(!config.hideMenuBar);
var autostart = this.state.autostart;
appLauncher.isEnabled().then(function(enabled) {
if (enabled && !autostart) {
appLauncher.disable();
} else if (!enabled && autostart) {
appLauncher.enable();
}
});
}
2016-05-25 13:18:48 -07:00
2016-05-21 00:13:22 -07:00
ipcRenderer.send('update-menu', config);
ipcRenderer.send('update-config');
2016-06-06 05:30:23 -07:00
backToIndex();
},
handleCancel: function() {
2016-01-30 07:50:43 -08:00
backToIndex();
},
2016-04-17 07:07:17 -07:00
handleChangeDisableWebSecurity: function() {
this.setState({
disablewebsecurity: this.refs.disablewebsecurity.getChecked()
});
},
handleChangeHideMenuBar: function() {
this.setState({
hideMenuBar: this.refs.hideMenuBar.getChecked()
});
},
2016-04-11 05:51:24 -07:00
handleChangeShowTrayIcon: function() {
var shouldShowTrayIcon = this.refs.showTrayIcon.getChecked();
2016-04-11 05:51:24 -07:00
this.setState({
showTrayIcon: shouldShowTrayIcon
2016-04-11 05:51:24 -07:00
});
if (process.platform === 'darwin' && !shouldShowTrayIcon) {
this.setState({
minimizeToTray: false
});
}
2016-04-11 05:51:24 -07:00
},
handleChangeTrayIconTheme: function() {
this.setState({
trayIconTheme: this.refs.trayIconTheme.getValue()
});
},
handleChangeAutoStart: function() {
this.setState({
autostart: this.refs.autostart.getChecked()
});
},
handleChangeMinimizeToTray: function() {
var shouldMinimizeToTray = (process.platform !== 'darwin' || this.refs.showTrayIcon.getChecked())
&& this.refs.minimizeToTray.getChecked();
this.setState({
minimizeToTray: shouldMinimizeToTray
});
},
handleChangeToggleWindowOnTrayIconClick: function() {
this.setState({
toggleWindowOnTrayIconClick: this.refs.toggleWindowOnTrayIconClick.getChecked()
});
},
toggleShowTeamForm: function() {
this.setState({
showAddTeamForm: !this.state.showAddTeamForm
});
2016-05-25 11:31:57 -07:00
},
2016-09-11 08:18:09 -07:00
handleFlashWindow: function() {
this.setState({
notifications: {
2016-09-11 08:18:09 -07:00
flashWindow: this.refs.flashWindow.getChecked() ? 2 : 0
}
});
},
2016-07-15 04:04:14 -07:00
handleShowUnreadBadge: function() {
this.setState({
showUnreadBadge: this.refs.showUnreadBadge.getChecked()
});
},
render: function() {
var teams_row = (
<Row>
<Col md={ 12 }>
2016-05-25 11:31:57 -07:00
<TeamList teams={ this.state.teams } showAddTeamForm={ this.state.showAddTeamForm } onTeamsChange={ this.handleTeamsChange } />
</Col>
</Row>
);
var options = [];
2016-01-23 04:33:50 -08:00
if (process.platform === 'win32' || process.platform === 'linux') {
2016-07-11 14:17:31 -07:00
options.push(<Input key="inputHideMenuBar" id="inputHideMenuBar" ref="hideMenuBar" type="checkbox" label="Hide menu bar (Press Alt to show menu bar)" checked={ this.state.hideMenuBar }
2016-05-02 08:42:13 -07:00
onChange={ this.handleChangeHideMenuBar } />);
}
if (process.platform === 'darwin' || process.platform === 'linux') {
2016-10-27 08:42:31 -07:00
options.push(<Input key="inputShowTrayIcon" id="inputShowTrayIcon" ref="showTrayIcon" type="checkbox" label={ process.platform === 'darwin' ? "Show icon on menu bar (need to restart the application)" : "Show icon in notification area (need to restart the application)" } checked={ this.state.showTrayIcon } onChange={ this.handleChangeShowTrayIcon }
/>);
2016-04-11 05:51:24 -07:00
}
if (process.platform === 'linux') {
options.push(<Input key="inputTrayIconTheme" ref="trayIconTheme" type="select" label="Icon theme (Need to restart the application)" value={ this.state.trayIconTheme } onChange={ this.handleChangeTrayIconTheme }>
<option value="light">Light</option>
<option value="dark">Dark</option>
</Input>);
}
options.push(<Input key="inputDisableWebSecurity" id="inputDisableWebSecurity" ref="disablewebsecurity" type="checkbox" label="Allow mixed content (Enabling allows both secure and insecure content, images and scripts to render and execute. Disabling allows only secure content.)"
checked={ this.state.disablewebsecurity } onChange={ this.handleChangeDisableWebSecurity } />);
//OSX has an option in the Dock, to set the app to autostart, so we choose to not support this option for OSX
if (process.platform === 'win32' || process.platform === 'linux') {
2016-06-22 08:03:04 -07:00
options.push(<Input key="inputAutoStart" id="inputAutoStart" ref="autostart" type="checkbox" label="Start app on login." checked={ this.state.autostart } onChange={ this.handleChangeAutoStart }
/>);
}
if (process.platform === 'darwin' || process.platform === 'linux') {
options.push(<Input key="inputMinimizeToTray" id="inputMinimizeToTray" ref="minimizeToTray" type="checkbox" label={ this.state.trayWasVisible || !this.state.showTrayIcon ? "Leave app running in notification area when the window is closed" : "Leave app running in notification area when the window is closed (available on next restart)" } disabled={ !this.state.showTrayIcon || !this.state.trayWasVisible } checked={ this.state.minimizeToTray }
onChange={ this.handleChangeMinimizeToTray } />);
}
if (process.platform === 'win32') {
2016-06-28 06:10:20 -07:00
options.push(<Input key="inputToggleWindowOnTrayIconClick" id="inputToggleWindowOnTrayIconClick" ref="toggleWindowOnTrayIconClick" type="checkbox" label="Toggle window visibility when clicking on the tray icon."
checked={ this.state.toggleWindowOnTrayIconClick } onChange={ this.handleChangeToggleWindowOnTrayIconClick } />);
}
2016-07-15 04:04:14 -07:00
if (process.platform === 'darwin' || process.platform === 'win32') {
options.push(<Input key="inputShowUnreadBadge" id="inputShowUnreadBadge" ref="showUnreadBadge" type="checkbox" label="Show red badge on taskbar icon to indicate unread messages. Regardless of this setting, mentions are always indicated with a red badge and item count on the taskbar icon."
checked={ this.state.showUnreadBadge } onChange={ this.handleShowUnreadBadge } />);
}
2016-09-11 08:18:09 -07:00
if (process.platform === 'win32' || process.platform === 'linux') {
options.push(<Input key="flashWindow" id="inputflashWindow" ref="flashWindow" type="checkbox" label="Flash the taskbar icon when a new message is received." checked={ this.state.notifications.flashWindow === 2 }
onChange={ this.handleFlashWindow } />);
}
2016-09-12 08:53:45 -07:00
const settingsPage = {
navbar: {
2016-09-13 18:25:41 -07:00
backgroundColor: '#fff'
2016-09-13 09:23:10 -07:00
},
close: {
position: 'absolute',
right: '0',
2016-09-13 18:25:41 -07:00
top: '10px',
fontSize: '35px',
2016-09-13 09:23:10 -07:00
fontWeight: 'normal',
color: '#bbb',
cursor: 'pointer'
2016-09-12 08:53:45 -07:00
},
heading: {
textAlign: 'center',
fontSize: '24px',
margin: '0',
2016-09-13 18:25:41 -07:00
padding: '1em 0'
2016-09-12 08:53:45 -07:00
},
sectionHeading: {
fontSize: '20px',
margin: '0',
padding: '1em 0'
},
sectionHeadingLink: {
marginTop: '24px',
display: 'inline-block',
fontSize: '15px'
},
footer: {
padding: '0.4em 0'
}
}
var options_row = (options.length > 0) ? (
<Row>
<Col md={ 12 }>
2016-09-12 08:53:45 -07:00
<h2 style={ settingsPage.sectionHeading }>App options</h2>
2016-02-11 08:12:28 -08:00
{ options }
</Col>
</Row>
) : null;
return (
2016-09-11 08:18:09 -07:00
<div>
2016-09-12 09:02:11 -07:00
<Navbar className="navbar-fixed-top" style={ settingsPage.navbar }>
2016-09-13 18:25:41 -07:00
<div style={ { 'position': 'relative' } }>
<h1 style={ settingsPage.heading }>Settings</h1>
<div style={ settingsPage.close } onClick={ this.handleCancel }>
<span>×</span>
</div>
</div>
2016-09-11 08:18:09 -07:00
</Navbar>
2016-09-13 09:23:10 -07:00
<Grid className="settingsPage" style={ { 'padding': '100px 15px' } }>
2016-09-11 08:18:09 -07:00
<Row>
<Col md={ 10 } xs={ 8 }>
2016-09-12 08:53:45 -07:00
<h2 style={ settingsPage.sectionHeading }>Team Management</h2>
2016-09-11 08:18:09 -07:00
</Col>
<Col md={ 2 } xs={ 4 }>
2016-09-12 08:53:45 -07:00
<p className="text-right"><a style={ settingsPage.sectionHeadingLink } href="#" onClick={ this.toggleShowTeamForm }> Add new team</a></p>
2016-09-11 08:18:09 -07:00
</Col>
</Row>
{ teams_row }
2016-09-12 08:53:45 -07:00
<hr/>
2016-09-11 08:18:09 -07:00
{ options_row }
</Grid>
<Navbar className="navbar-fixed-bottom">
2016-09-12 08:53:45 -07:00
<div className='text-right' style={ settingsPage.footer }>
<button id="btnCancel" className="btn btn-link" onClick={ this.handleCancel }>Cancel</button>
2016-09-11 08:18:09 -07:00
{ ' ' }
<button id="btnSave" className="btn btn-primary navbar-btn" bsStyle="primary" onClick={ this.handleSave } disabled={ this.state.teams.length === 0 }>Save</button>
2016-09-12 08:53:45 -07:00
</div>
2016-09-11 08:18:09 -07:00
</Navbar>
</div>
2015-12-19 07:39:51 -08:00
);
}
});
var TeamList = React.createClass({
2016-05-25 11:31:57 -07:00
getInitialState: function() {
return {
2016-05-25 13:18:48 -07:00
showTeamListItemNew: false,
team: {
url: '',
name: '',
index: false
}
2016-05-25 11:31:57 -07:00
};
},
2015-12-21 07:58:41 -08:00
handleTeamRemove: function(index) {
console.log(index);
var teams = this.props.teams;
2015-12-21 07:58:41 -08:00
teams.splice(index, 1);
this.props.onTeamsChange(teams);
},
2015-12-21 07:58:41 -08:00
handleTeamAdd: function(team) {
var teams = this.props.teams;
2016-05-25 13:18:48 -07:00
// check if team already exists and then change existing team or add new one
2016-06-18 04:40:12 -07:00
if ((team.index !== undefined) && teams[team.index]) {
2016-05-25 13:18:48 -07:00
teams[team.index].name = team.name;
teams[team.index].url = team.url;
} else {
teams.push(team);
}
this.setState({
showTeamListItemNew: false,
team: {
url: '',
name: '',
index: false
}
2016-05-25 13:18:48 -07:00
});
this.props.onTeamsChange(teams);
},
2016-05-25 13:18:48 -07:00
handleTeamEditing: function(teamName, teamUrl, teamIndex) {
this.setState({
showTeamListItemNew: true,
team: {
url: teamUrl,
name: teamName,
index: teamIndex
}
})
},
render: function() {
var thisObj = this;
2015-12-19 07:39:51 -08:00
var teamNodes = this.props.teams.map(function(team, i) {
2015-12-21 07:58:41 -08:00
var handleTeamRemove = function() {
thisObj.handleTeamRemove(i);
};
2016-05-25 13:18:48 -07:00
var handleTeamEditing = function() {
thisObj.handleTeamEditing(team.name, team.url, i);
};
return (
2016-05-25 13:28:47 -07:00
<TeamListItem index={ i } key={ "teamListItem" + i } name={ team.name } url={ team.url } onTeamRemove={ handleTeamRemove } onTeamEditing={ handleTeamEditing }
/>
2015-12-19 07:39:51 -08:00
);
});
2016-05-25 11:31:57 -07:00
var addTeamForm;
2016-05-25 13:18:48 -07:00
if (this.props.showAddTeamForm || this.state.showTeamListItemNew) {
addTeamForm = <TeamListItemNew key={ this.state.team.index } onTeamAdd={ this.handleTeamAdd } teamIndex={ this.state.team.index } teamName={ this.state.team.name } teamUrl={ this.state.team.url }
/>;
2016-05-25 11:31:57 -07:00
} else {
addTeamForm = '';
}
return (
2015-12-21 07:58:41 -08:00
<ListGroup class="teamList">
{ teamNodes }
2016-05-25 11:31:57 -07:00
{ addTeamForm }
2015-12-21 07:58:41 -08:00
</ListGroup>
2015-12-19 07:39:51 -08:00
);
}
});
2015-12-21 07:58:41 -08:00
var TeamListItem = React.createClass({
handleTeamRemove: function() {
this.props.onTeamRemove();
},
2016-05-25 13:18:48 -07:00
handleTeamEditing: function() {
this.props.onTeamEditing();
},
render: function() {
2015-12-21 07:58:41 -08:00
var style = {
left: {
"display": 'inline-block'
}
};
return (
2015-12-21 07:58:41 -08:00
<div className="teamListItem list-group-item">
<div style={ style.left }>
<h4 className="list-group-item-heading">{ this.props.name }</h4>
<p className="list-group-item-text">
{ this.props.url }
</p>
</div>
<div className="pull-right">
<a href="#" onClick={ this.handleTeamEditing }>Edit</a>
2016-09-11 08:18:09 -07:00
{ ' - ' }
<a href="#" onClick={ this.handleTeamRemove }>Remove</a>
2015-12-21 07:58:41 -08:00
</div>
</div>
2015-12-19 07:39:51 -08:00
);
}
});
2015-12-21 07:58:41 -08:00
var TeamListItemNew = React.createClass({
2015-12-19 07:39:51 -08:00
getInitialState: function() {
return {
2016-05-25 13:18:48 -07:00
name: this.props.teamName,
url: this.props.teamUrl,
index: this.props.teamIndex,
errorMessage: null
2015-12-19 07:39:51 -08:00
};
},
2015-12-21 07:58:41 -08:00
handleSubmit: function(e) {
console.log('submit');
e.preventDefault();
const errorMessage = this.getValidationErrorMessage();
if (errorMessage) {
this.setState({
errorMessage
});
return;
}
2015-12-21 07:58:41 -08:00
this.props.onTeamAdd({
name: this.state.name.trim(),
2016-05-25 13:18:48 -07:00
url: this.state.url.trim(),
index: this.state.index,
2015-12-19 07:39:51 -08:00
});
this.setState({
name: '',
url: '',
index: '',
errorMessage: null
2015-12-19 07:39:51 -08:00
});
},
2015-12-19 07:39:51 -08:00
handleNameChange: function(e) {
2015-12-21 07:58:41 -08:00
console.log('name');
2015-12-19 07:39:51 -08:00
this.setState({
name: e.target.value
});
},
2015-12-19 07:39:51 -08:00
handleURLChange: function(e) {
2015-12-21 07:58:41 -08:00
console.log('url');
2015-12-19 07:39:51 -08:00
this.setState({
url: e.target.value
});
},
getValidationErrorMessage: function() {
if (this.state.name.trim() === '') {
return 'Name is required.';
} else if (this.state.url.trim() === '') {
return 'URL is required.';
} else if (!(/^https?:\/\/.*/).test(this.state.url.trim())) {
return 'URL should start with http:// or https://.';
}
return null;
},
componentDidMount: function() {
const inputTeamName = ReactDOM.findDOMNode(this.refs.inputTeamName);
const setErrorMessage = () => {
this.setState({
errorMessage: this.getValidationErrorMessage()
});
};
inputTeamName.addEventListener('invalid', setErrorMessage);
const inputTeamURL = ReactDOM.findDOMNode(this.refs.inputTeamURL);
inputTeamURL.addEventListener('invalid', setErrorMessage);
},
render: function() {
2016-05-25 13:18:48 -07:00
var existingTeam = false;
if (this.state.name !== '' && this.state.url !== '') {
existingTeam = true;
}
var btnAddText;
if (existingTeam) {
btnAddText = 'Save';
} else {
btnAddText = 'Add';
}
return (
2015-12-21 07:58:41 -08:00
<ListGroupItem>
<form className="form-inline" onSubmit={ this.handleSubmit }>
<div className="form-group">
<label for="inputTeamName">Name</label>
{ ' ' }
<input type="text" required className="form-control" id="inputTeamName" ref="inputTeamName" placeholder="Example team" value={ this.state.name } onChange={ this.handleNameChange }
/>
2015-12-21 07:58:41 -08:00
</div>
{ ' ' }
<div className="form-group">
<label for="inputTeamURL">URL</label>
{ ' ' }
<input type="url" required className="form-control" id="inputTeamURL" ref="inputTeamURL" placeholder="https://example.com/team" value={ this.state.url } onChange={ this.handleURLChange }
/>
2015-12-21 07:58:41 -08:00
</div>
{ ' ' }
<Button type="submit">
2016-05-25 13:28:47 -07:00
{ btnAddText }
</Button>
2015-12-21 07:58:41 -08:00
</form>
{ (() => {
if (this.state.errorMessage !== null) {
return (<HelpBlock style={ { color: '#777777' } }>
{ this.state.errorMessage }
</HelpBlock>);
}
return null;
})() }
2015-12-21 07:58:41 -08:00
</ListGroupItem>
2015-12-19 07:39:51 -08:00
);
}
});
var configFile = remote.getGlobal('config-file');
require('electron-context-menu')({
window: remote.getCurrentWindow()
});
ReactDOM.render(
2015-12-19 07:39:51 -08:00
<SettingsPage configFile={ configFile } />,
document.getElementById('content')
);