[MM-22073] Added new titlebar to Settings page (#1171)
This commit is contained in:
@@ -6,15 +6,23 @@
|
|||||||
/* eslint-disable react/no-set-state */
|
/* eslint-disable react/no-set-state */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import {Button, Checkbox, Col, FormGroup, Grid, HelpBlock, Navbar, Radio, Row} from 'react-bootstrap';
|
import {Button, Checkbox, Col, FormGroup, Grid, HelpBlock, Navbar, Radio, Row} from 'react-bootstrap';
|
||||||
|
|
||||||
import {ipcRenderer, remote} from 'electron';
|
import {ipcRenderer, remote} from 'electron';
|
||||||
import {debounce} from 'underscore';
|
import {debounce} from 'underscore';
|
||||||
|
import DotsVerticalIcon from 'mdi-react/DotsVerticalIcon';
|
||||||
|
|
||||||
import Config from '../../common/config';
|
import Config from '../../common/config';
|
||||||
|
|
||||||
|
import restoreButton from '../../assets/titlebar/chrome-restore.svg';
|
||||||
|
import maximizeButton from '../../assets/titlebar/chrome-maximize.svg';
|
||||||
|
import minimizeButton from '../../assets/titlebar/chrome-minimize.svg';
|
||||||
|
import closeButton from '../../assets/titlebar/chrome-close.svg';
|
||||||
|
|
||||||
import TeamList from './TeamList.jsx';
|
import TeamList from './TeamList.jsx';
|
||||||
import AutoSaveIndicator from './AutoSaveIndicator.jsx';
|
import AutoSaveIndicator from './AutoSaveIndicator.jsx';
|
||||||
|
import TabBar from './TabBar.jsx';
|
||||||
|
|
||||||
const CONFIG_TYPE_SERVERS = 'servers';
|
const CONFIG_TYPE_SERVERS = 'servers';
|
||||||
const CONFIG_TYPE_APP_OPTIONS = 'appOptions';
|
const CONFIG_TYPE_APP_OPTIONS = 'appOptions';
|
||||||
@@ -32,6 +40,9 @@ export default class SettingsPage extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = this.convertConfigDataToState(config.data);
|
this.state = this.convertConfigDataToState(config.data);
|
||||||
|
this.setState({
|
||||||
|
maximized: false,
|
||||||
|
});
|
||||||
|
|
||||||
this.trayIconThemeRef = React.createRef();
|
this.trayIconThemeRef = React.createRef();
|
||||||
|
|
||||||
@@ -43,6 +54,7 @@ export default class SettingsPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
const self = this;
|
||||||
config.on('update', (configData) => {
|
config.on('update', (configData) => {
|
||||||
this.updateSaveState();
|
this.updateSaveState();
|
||||||
this.setState(this.convertConfigDataToState(configData, this.state));
|
this.setState(this.convertConfigDataToState(configData, this.state));
|
||||||
@@ -60,6 +72,29 @@ export default class SettingsPage extends React.Component {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function focusListener() {
|
||||||
|
self.setState({unfocused: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
function blurListener() {
|
||||||
|
self.setState({unfocused: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentWindow = remote.getCurrentWindow();
|
||||||
|
currentWindow.on('focus', focusListener);
|
||||||
|
currentWindow.on('blur', blurListener);
|
||||||
|
if (currentWindow.isMaximized()) {
|
||||||
|
self.setState({maximized: true});
|
||||||
|
}
|
||||||
|
currentWindow.on('maximize', this.handleMaximizeState);
|
||||||
|
currentWindow.on('unmaximize', this.handleMaximizeState);
|
||||||
|
|
||||||
|
if (currentWindow.isFullScreen()) {
|
||||||
|
self.setState({fullScreen: true});
|
||||||
|
}
|
||||||
|
currentWindow.on('enter-full-screen', this.handleFullScreenState);
|
||||||
|
currentWindow.on('leave-full-screen', this.handleFullScreenState);
|
||||||
|
|
||||||
// when the config object changes here in the renderer process, tell the main process to reload its config object to get the changes
|
// when the config object changes here in the renderer process, tell the main process to reload its config object to get the changes
|
||||||
config.on('synchronize', () => {
|
config.on('synchronize', () => {
|
||||||
ipcRenderer.send('reload-config');
|
ipcRenderer.send('reload-config');
|
||||||
@@ -157,6 +192,32 @@ export default class SettingsPage extends React.Component {
|
|||||||
}
|
}
|
||||||
activeTabWebContents.pasteAndMatchStyle();
|
activeTabWebContents.pasteAndMatchStyle();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
self.setState({
|
||||||
|
isDarkMode: remote.systemPreferences.isDarkMode(),
|
||||||
|
});
|
||||||
|
remote.systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => {
|
||||||
|
self.setState({
|
||||||
|
isDarkMode: remote.systemPreferences.isDarkMode(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.setState({
|
||||||
|
isDarkMode: this.props.getDarkMode(),
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('set-dark-mode', () => {
|
||||||
|
this.setDarkMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.threeDotMenu = React.createRef();
|
||||||
|
ipcRenderer.on('focus-three-dot-menu', () => {
|
||||||
|
if (this.threeDotMenu.current) {
|
||||||
|
this.threeDotMenu.current.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
convertConfigDataToState = (configData, currentState = {}) => {
|
convertConfigDataToState = (configData, currentState = {}) => {
|
||||||
@@ -373,10 +434,158 @@ export default class SettingsPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDarkMode() {
|
||||||
|
this.setState({
|
||||||
|
isDarkMode: this.props.setDarkMode(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClose = () => {
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMinimize = () => {
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
win.minimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMaximize = () => {
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
win.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRestore = () => {
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
win.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
openMenu = () => {
|
||||||
|
// @eslint-ignore
|
||||||
|
this.threeDotMenu.current.blur();
|
||||||
|
this.props.openMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDoubleClick = () => {
|
||||||
|
const doubleClickAction = remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
if (doubleClickAction === 'Minimize') {
|
||||||
|
win.minimize();
|
||||||
|
} else if (doubleClickAction === 'Maximize' && !win.isMaximized()) {
|
||||||
|
win.maximize();
|
||||||
|
} else if (doubleClickAction === 'Maximize' && win.isMaximized()) {
|
||||||
|
win.unmaximize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMaximizeState = () => {
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
this.setState({maximized: win.isMaximized()});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFullScreenState = () => {
|
||||||
|
const win = remote.getCurrentWindow();
|
||||||
|
this.setState({fullScreen: win.isFullScreen()});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const tabsRow = (
|
||||||
|
<TabBar
|
||||||
|
id='tabBar'
|
||||||
|
isDarkMode={this.state.isDarkMode}
|
||||||
|
teams={[]}
|
||||||
|
showAddServerButton={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
let topBarClassName = 'topBar';
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
topBarClassName += ' macOS';
|
||||||
|
}
|
||||||
|
if (this.state.isDarkMode) {
|
||||||
|
topBarClassName += ' darkMode';
|
||||||
|
}
|
||||||
|
if (this.state.fullScreen) {
|
||||||
|
topBarClassName += ' fullScreen';
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxButton;
|
||||||
|
if (this.state.maximized) {
|
||||||
|
maxButton = (
|
||||||
|
<div
|
||||||
|
className='button restore-button'
|
||||||
|
onClick={this.handleRestore}
|
||||||
|
>
|
||||||
|
<img src={restoreButton}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
maxButton = (
|
||||||
|
<div
|
||||||
|
className='button max-button'
|
||||||
|
onClick={this.handleMaximize}
|
||||||
|
>
|
||||||
|
<img src={maximizeButton}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let overlayGradient;
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
overlayGradient = (
|
||||||
|
<span className='overlay-gradient'/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleBarButtons;
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
titleBarButtons = (
|
||||||
|
<span className='title-bar-btns'>
|
||||||
|
<div
|
||||||
|
className='button min-button'
|
||||||
|
onClick={this.handleMinimize}
|
||||||
|
>
|
||||||
|
<img src={minimizeButton}/>
|
||||||
|
</div>
|
||||||
|
{maxButton}
|
||||||
|
<div
|
||||||
|
className='button close-button'
|
||||||
|
onClick={this.handleClose}
|
||||||
|
>
|
||||||
|
<img src={closeButton}/>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const topRow = (
|
||||||
|
<Row
|
||||||
|
className={topBarClassName}
|
||||||
|
onDoubleClick={this.handleDoubleClick}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={this.topBar}
|
||||||
|
className={`topBar-bg${this.state.unfocused ? ' unfocused' : ''}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className='three-dot-menu'
|
||||||
|
onClick={this.openMenu}
|
||||||
|
tabIndex={0}
|
||||||
|
ref={this.threeDotMenu}
|
||||||
|
>
|
||||||
|
<DotsVerticalIcon/>
|
||||||
|
</button>
|
||||||
|
{tabsRow}
|
||||||
|
{overlayGradient}
|
||||||
|
{titleBarButtons}
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
|
||||||
const settingsPage = {
|
const settingsPage = {
|
||||||
navbar: {
|
navbar: {
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
|
position: 'relative',
|
||||||
},
|
},
|
||||||
close: {
|
close: {
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
@@ -688,35 +897,54 @@ export default class SettingsPage extends React.Component {
|
|||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='modal-container'>
|
<div
|
||||||
<Navbar
|
className='container-fluid'
|
||||||
className='navbar-fixed-top'
|
style={{
|
||||||
style={settingsPage.navbar}
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ topRow }
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
overflowY: 'auto',
|
||||||
|
height: '100%',
|
||||||
|
margin: '0 -15px',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div style={{position: 'relative'}}>
|
<Navbar
|
||||||
<h1 style={settingsPage.heading}>{'Settings'}</h1>
|
className='navbar-fixed-top'
|
||||||
<Button
|
style={settingsPage.navbar}
|
||||||
id='btnClose'
|
>
|
||||||
className='CloseButton'
|
<div style={{position: 'relative'}}>
|
||||||
bsStyle='link'
|
<h1 style={settingsPage.heading}>{'Settings'}</h1>
|
||||||
style={settingsPage.close}
|
<Button
|
||||||
onClick={this.handleCancel}
|
id='btnClose'
|
||||||
disabled={this.state.teams.length === 0}
|
className='CloseButton'
|
||||||
>
|
bsStyle='link'
|
||||||
<span>{'×'}</span>
|
style={settingsPage.close}
|
||||||
</Button>
|
onClick={this.handleCancel}
|
||||||
</div>
|
disabled={this.state.teams.length === 0}
|
||||||
</Navbar>
|
>
|
||||||
<Grid
|
<span>{'×'}</span>
|
||||||
className='settingsPage'
|
</Button>
|
||||||
style={{paddingTop: '100px'}}
|
</div>
|
||||||
>
|
</Navbar>
|
||||||
{ srvMgmt }
|
<Grid
|
||||||
{ optionsRow }
|
className='settingsPage'
|
||||||
</Grid>
|
>
|
||||||
|
{ srvMgmt }
|
||||||
|
{ optionsRow }
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsPage.propTypes = {
|
||||||
|
getDarkMode: PropTypes.func.isRequired,
|
||||||
|
setDarkMode: PropTypes.func.isRequired,
|
||||||
|
openMenu: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
/* eslint-enable react/no-set-state */
|
/* eslint-enable react/no-set-state */
|
||||||
|
@@ -20,3 +20,12 @@
|
|||||||
.checkbox > label {
|
.checkbox > label {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-2016 Yuya Ochiai
|
// Copyright (c) 2015-2016 Yuya Ochiai
|
||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import {remote} from 'electron';
|
import {remote, ipcRenderer} from 'electron';
|
||||||
|
|
||||||
window.eval = global.eval = () => { // eslint-disable-line no-multi-assign, no-eval
|
window.eval = global.eval = () => { // eslint-disable-line no-multi-assign, no-eval
|
||||||
throw new Error(`Sorry, ${remote.app.getName()} does not support window.eval() for security reasons.`);
|
throw new Error(`Sorry, ${remote.app.getName()} does not support window.eval() for security reasons.`);
|
||||||
@@ -10,13 +10,43 @@ window.eval = global.eval = () => { // eslint-disable-line no-multi-assign, no-e
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import Config from '../common/config';
|
||||||
|
|
||||||
import SettingsPage from './components/SettingsPage.jsx';
|
import SettingsPage from './components/SettingsPage.jsx';
|
||||||
import contextMenu from './js/contextMenu';
|
import contextMenu from './js/contextMenu';
|
||||||
|
|
||||||
contextMenu.setup(remote.getCurrentWindow());
|
contextMenu.setup(remote.getCurrentWindow());
|
||||||
|
|
||||||
|
const config = new Config(remote.app.getPath('userData') + '/config.json', remote.getCurrentWindow().registryConfigData);
|
||||||
|
|
||||||
|
function getDarkMode() {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
return config.darkMode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDarkMode() {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
const darkMode = Boolean(config.darkMode);
|
||||||
|
config.set('darkMode', !darkMode);
|
||||||
|
return !darkMode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMenu() {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
ipcRenderer.send('open-app-menu');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<SettingsPage/>,
|
<SettingsPage
|
||||||
|
getDarkMode={getDarkMode}
|
||||||
|
setDarkMode={setDarkMode}
|
||||||
|
openMenu={openMenu}
|
||||||
|
/>,
|
||||||
document.getElementById('content')
|
document.getElementById('content')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user