mattermost-desktop/src/browser/settings.jsx

678 lines
18 KiB
React
Raw Normal View History

'use strict';
2016-09-25 07:14:01 -07:00
window.eval = global.eval = () => {
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-25 07:14:01 -07:00
const {Grid, Row, Col, Input, Button, ListGroup, ListGroupItem, HelpBlock, Navbar} = require('react-bootstrap');
var AutoLaunch = require('auto-launch');
2016-01-30 07:50:43 -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({
2016-09-25 07:14:01 -07:00
getInitialState() {
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;
},
2016-09-25 07:14:01 -07:00
componentDidMount() {
if (process.platform === 'win32' || process.platform === 'linux') {
var self = this;
2016-09-25 07:14:01 -07:00
appLauncher.isEnabled().then((enabled) => {
self.setState({
autostart: enabled
});
});
}
},
2016-09-25 07:14:01 -07:00
handleTeamsChange(teams) {
2015-12-19 07:39:51 -08:00
this.setState({
showAddTeamForm: false,
2016-09-25 07:14:01 -07:00
teams
2015-12-19 07:39:51 -08:00
});
},
2016-09-25 07:14:01 -07:00
handleSave() {
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;
2016-09-25 07:14:01 -07:00
appLauncher.isEnabled().then((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();
},
2016-09-25 07:14:01 -07:00
handleCancel() {
2016-01-30 07:50:43 -08:00
backToIndex();
},
2016-09-25 07:14:01 -07:00
handleChangeDisableWebSecurity() {
2016-04-17 07:07:17 -07:00
this.setState({
disablewebsecurity: this.refs.disablewebsecurity.getChecked()
});
},
2016-09-25 07:14:01 -07:00
handleChangeHideMenuBar() {
this.setState({
hideMenuBar: this.refs.hideMenuBar.getChecked()
});
},
2016-09-25 07:14:01 -07:00
handleChangeShowTrayIcon() {
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
},
2016-09-25 07:14:01 -07:00
handleChangeTrayIconTheme() {
this.setState({
trayIconTheme: this.refs.trayIconTheme.getValue()
});
},
2016-09-25 07:14:01 -07:00
handleChangeAutoStart() {
this.setState({
autostart: this.refs.autostart.getChecked()
});
},
2016-09-25 07:14:01 -07:00
handleChangeMinimizeToTray() {
var shouldMinimizeToTray =
(process.platform !== 'darwin' || this.refs.showTrayIcon.getChecked()) &&
this.refs.minimizeToTray.getChecked();
this.setState({
minimizeToTray: shouldMinimizeToTray
});
},
2016-09-25 07:14:01 -07:00
handleChangeToggleWindowOnTrayIconClick() {
this.setState({
toggleWindowOnTrayIconClick: this.refs.toggleWindowOnTrayIconClick.getChecked()
});
},
2016-09-25 07:14:01 -07:00
toggleShowTeamForm() {
this.setState({
showAddTeamForm: !this.state.showAddTeamForm
});
2016-05-25 11:31:57 -07:00
},
2016-09-25 07:14:01 -07:00
handleFlashWindow() {
this.setState({
notifications: {
2016-09-11 08:18:09 -07:00
flashWindow: this.refs.flashWindow.getChecked() ? 2 : 0
}
});
},
2016-09-25 07:14:01 -07:00
handleShowUnreadBadge() {
2016-07-15 04:04:14 -07:00
this.setState({
showUnreadBadge: this.refs.showUnreadBadge.getChecked()
});
},
2016-09-25 07:14:01 -07:00
render() {
var teamsRow = (
<Row>
<Col md={12}>
<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-09-25 07:14:01 -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}
onChange={this.handleChangeHideMenuBar}
/>);
}
if (process.platform === 'darwin' || process.platform === 'linux') {
2016-09-25 07:14:01 -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') {
2016-09-25 07:14:01 -07:00
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>);
}
2016-09-25 07:14:01 -07:00
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-09-25 07:14:01 -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') {
2016-09-25 07:14:01 -07:00
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-09-25 07:14:01 -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') {
2016-09-25 07:14:01 -07:00
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-07-15 04:04:14 -07:00
}
2016-09-11 08:18:09 -07:00
if (process.platform === 'win32' || process.platform === 'linux') {
2016-09-25 07:14:01 -07:00
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-11 08:18:09 -07:00
}
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'
}
2016-09-25 07:14:01 -07:00
};
2016-09-12 08:53:45 -07:00
2016-09-25 07:14:01 -07:00
var optionsRow = (options.length > 0) ? (
<Row>
2016-09-25 07:14:01 -07:00
<Col md={12}>
<h2 style={settingsPage.sectionHeading}>{'App options'}</h2>
{ options }
</Col>
</Row>
) : null;
return (
2016-09-11 08:18:09 -07:00
<div>
2016-09-25 07:14:01 -07:00
<Navbar
className='navbar-fixed-top'
style={settingsPage.navbar}
>
<div style={{position: 'relative'}}>
<h1 style={settingsPage.heading}>{'Settings'}</h1>
<div
style={settingsPage.close}
onClick={this.handleCancel}
>
<span>{'×'}</span>
2016-09-13 18:25:41 -07:00
</div>
</div>
2016-09-11 08:18:09 -07:00
</Navbar>
2016-09-25 07:14:01 -07:00
<Grid
className='settingsPage'
style={{padding: '100px 15px'}}
>
2016-09-11 08:18:09 -07:00
<Row>
2016-09-25 07:14:01 -07:00
<Col
md={10}
xs={8}
>
<h2 style={settingsPage.sectionHeading}>{'Team Management'}</h2>
2016-09-11 08:18:09 -07:00
</Col>
2016-09-25 07:14:01 -07:00
<Col
md={2}
xs={4}
>
<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>
2016-09-25 07:14:01 -07:00
{ teamsRow }
2016-09-12 08:53:45 -07:00
<hr/>
2016-09-25 07:14:01 -07:00
{ optionsRow }
2016-09-11 08:18:09 -07:00
</Grid>
2016-09-25 07:14:01 -07:00
<Navbar className='navbar-fixed-bottom'>
<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
{ ' ' }
2016-09-25 07:14:01 -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-09-25 07:14:01 -07:00
getInitialState() {
2016-05-25 11:31:57 -07:00
return {
2016-05-25 13:18:48 -07:00
showTeamListItemNew: false,
team: {
url: '',
name: '',
index: false
}
2016-05-25 11:31:57 -07:00
};
},
2016-09-25 07:14:01 -07:00
handleTeamRemove(index) {
2015-12-21 07:58:41 -08:00
console.log(index);
var teams = this.props.teams;
2015-12-21 07:58:41 -08:00
teams.splice(index, 1);
this.props.onTeamsChange(teams);
},
2016-09-25 07:14:01 -07:00
handleTeamAdd(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-09-25 07:14:01 -07:00
if ((typeof 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-09-25 07:14:01 -07:00
handleTeamEditing(teamName, teamUrl, teamIndex) {
2016-05-25 13:18:48 -07:00
this.setState({
showTeamListItemNew: true,
team: {
url: teamUrl,
name: teamName,
index: teamIndex
}
2016-09-25 07:14:01 -07:00
});
2016-05-25 13:18:48 -07:00
},
2016-09-25 07:14:01 -07:00
render() {
var self = this;
var teamNodes = this.props.teams.map((team, i) => {
function handleTeamRemove() {
self.handleTeamRemove(i);
}
2016-05-25 13:18:48 -07:00
2016-09-25 07:14:01 -07:00
function handleTeamEditing() {
self.handleTeamEditing(team.name, team.url, i);
}
2016-05-25 13:18:48 -07:00
return (
2016-09-25 07:14:01 -07:00
<TeamListItem
index={i}
key={'teamListItem' + i}
name={team.name}
url={team.url}
onTeamRemove={handleTeamRemove}
onTeamEditing={handleTeamEditing}
2016-05-25 13:28:47 -07:00
/>
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) {
2016-09-25 07:14:01 -07:00
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 (
2016-09-25 07:14:01 -07:00
<ListGroup class='teamList'>
2015-12-21 07:58:41 -08:00
{ 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({
2016-09-25 07:14:01 -07:00
handleTeamRemove() {
2015-12-21 07:58:41 -08:00
this.props.onTeamRemove();
},
2016-09-25 07:14:01 -07:00
handleTeamEditing() {
2016-05-25 13:18:48 -07:00
this.props.onTeamEditing();
},
2016-09-25 07:14:01 -07:00
render() {
2015-12-21 07:58:41 -08:00
var style = {
left: {
2016-09-25 07:14:01 -07:00
display: 'inline-block'
2015-12-21 07:58:41 -08:00
}
};
return (
2016-09-25 07:14:01 -07: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'>
2015-12-21 07:58:41 -08:00
{ this.props.url }
</p>
</div>
2016-09-25 07:14:01 -07:00
<div className='pull-right'>
<a
href='#'
onClick={this.handleTeamEditing}
>{'Edit'}</a>
{' - '}
<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({
2016-09-25 07:14:01 -07:00
getInitialState() {
2015-12-19 07:39:51 -08:00
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
};
},
2016-09-25 07:14:01 -07:00
handleSubmit(e) {
2015-12-21 07:58:41 -08:00
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(),
2016-09-25 07:14:01 -07:00
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
});
},
2016-09-25 07:14:01 -07:00
handleNameChange(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
});
},
2016-09-25 07:14:01 -07:00
handleURLChange(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
});
},
2016-09-25 07:14:01 -07:00
getValidationErrorMessage() {
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;
},
2016-09-25 07:14:01 -07:00
componentDidMount() {
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);
},
2016-09-25 07:14:01 -07:00
render() {
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>
2016-09-25 07:14:01 -07:00
<form
className='form-inline'
onSubmit={this.handleSubmit}
>
<div className='form-group'>
<label htmlFor='inputTeamName'>{'Name'}</label>
2015-12-21 07:58:41 -08:00
{ ' ' }
2016-09-25 07:14:01 -07:00
<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>
{ ' ' }
2016-09-25 07:14:01 -07:00
<div className='form-group'>
<label htmlFor='inputTeamURL'>{'URL'}</label>
2015-12-21 07:58:41 -08:00
{ ' ' }
2016-09-25 07:14:01 -07:00
<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>
{ ' ' }
2016-09-25 07:14:01 -07:00
<Button type='submit'>
2016-05-25 13:28:47 -07:00
{ btnAddText }
</Button>
2015-12-21 07:58:41 -08:00
</form>
{ (() => {
2016-09-25 07:14:01 -07:00
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(
2016-09-25 07:14:01 -07:00
<SettingsPage configFile={configFile}/>,
document.getElementById('content')
);