diff options
| -rw-r--r-- | app/app.js | 18 | ||||
| -rw-r--r-- | app/components/Tray.js | 28 | ||||
| -rw-r--r-- | app/containers/Tray.js | 14 | ||||
| -rw-r--r-- | app/lib/components/TrayMenu.js | 122 | ||||
| -rw-r--r-- | app/main.js | 4 | ||||
| -rw-r--r-- | app/tray.js | 24 |
6 files changed, 178 insertions, 32 deletions
diff --git a/app/app.js b/app/app.js index 101014e9b1..eb71a8201b 100644 --- a/app/app.js +++ b/app/app.js @@ -3,18 +3,28 @@ import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { Router, hashHistory } from 'react-router'; import { syncHistoryWithStore } from 'react-router-redux'; +import { remote } from 'electron'; +import path from 'path'; import routes from './routes'; import configureStore from './store'; +import Tray from './containers/Tray'; + +const iconPath = path.join(__dirname, 'assets/trayicon.png'); +const tray = new remote.Tray(iconPath); const initialState = {}; const store = configureStore(initialState); const routerHistory = syncHistoryWithStore(hashHistory, store); - const rootElement = document.querySelector(document.currentScript.getAttribute('data-container')); ReactDOM.render( - <Provider store={store}> - <Router history={routerHistory} routes={routes} /> - </Provider>, + <div> + <Provider store={store}> + <Router history={routerHistory} routes={routes} /> + </Provider> + <Provider store={store}> + <Tray handle={tray} history={routerHistory} /> + </Provider> + </div>, rootElement ); diff --git a/app/components/Tray.js b/app/components/Tray.js new file mode 100644 index 0000000000..69f14cb7ca --- /dev/null +++ b/app/components/Tray.js @@ -0,0 +1,28 @@ +import React, { Component, PropTypes } from 'react'; +import { TrayMenu, TrayItem } from '../lib/components/TrayMenu'; + +export default class Tray extends Component { + + static propTypes = { + handle: PropTypes.object.isRequired, + } + + logout() { + this.props.login({ username: '', loggedIn: false }); + this.props.history.push('/'); + } + + render() { + const loggedIn = this.props.user && this.props.user.loggedIn; + + return ( + <TrayMenu tray={this.props.handle}> + {<TrayItem label="Log out" click={::this.logout} visible={loggedIn} />} + {<TrayItem type="separator" visible={loggedIn} />} + <TrayItem label="Privacy Policy" /> + <TrayItem label="Visit homepage" /> + </TrayMenu> + ); + } + +}
\ No newline at end of file diff --git a/app/containers/Tray.js b/app/containers/Tray.js new file mode 100644 index 0000000000..3916263d0d --- /dev/null +++ b/app/containers/Tray.js @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import Tray from '../components/Tray'; +import userActions from '../actions/user'; + +const mapStateToProps = (state) => { + return state; +}; + +const mapDispatchToProps = (dispatch) => { + return bindActionCreators(userActions, dispatch); +}; + +export default connect(mapStateToProps, mapDispatchToProps)(Tray); diff --git a/app/lib/components/TrayMenu.js b/app/lib/components/TrayMenu.js new file mode 100644 index 0000000000..bd4a68458a --- /dev/null +++ b/app/lib/components/TrayMenu.js @@ -0,0 +1,122 @@ +/** + * Declarative Tray implementation for React + Electron + */ + +import React, { Component, PropTypes } from 'react'; +import { remote } from 'electron'; + +const { Menu, MenuItem } = remote; + +/** + * Tray menu component + * + * Example: + * + * const tray = new remote.Tray('/path/to/icon'); + * + * return ( + * <TrayMenu tray={tray}> + * <TrayItem label="Visit homepage" /> + * </TrayMenu> + * ) + */ +class TrayMenu extends Component { + + static childContextTypes = { + menu: PropTypes.object.isRequired + }; + + static propTypes = { + tray: PropTypes.object.isRequired, + children: PropTypes.arrayOf(PropTypes.element).isRequired + }; + + _contextMenu = null; + + getChildContext() { + return { menu: this._contextMenu }; + } + + componentDidMount() { + this.props.tray.setContextMenu(this._contextMenu); + } + + componentDidUpdate() { + this.props.tray.setContextMenu(this._contextMenu); + } + + render() { + // create new menu during each rendering + // see: https://github.com/electron/electron/issues/8598 + this._contextMenu = new Menu(); + + return ( + <div>{this.props.children}</div> + ); + } + +} + +/** + * Submenu component + * + * Example: + * + * <TrayMenu tray={this.props.handle}> + * <TraySubmenu label="Resources"> + * <TrayItem label="Homepage" /> + * </TraySubmenu> + * </TrayMenu> + * + */ +class TraySubmenu extends Component { + + static contextTypes = { + menu: PropTypes.object.isRequired + }; + + static childContextTypes = { + menu: PropTypes.object.isRequired + }; + + static propTypes = { + children: PropTypes.arrayOf(PropTypes.element).isRequired + }; + + _contextMenu = null; + + getChildContext() { + return { menu: this._contextMenu }; + } + + render() { + // create new menu during each rendering + // see: https://github.com/electron/electron/issues/8598 + this.contextMenu = new Menu(); + + this.context.menu.append(new MenuItem({ ...this.props, submenu: this._contextMenu })); + + return ( + <div>{this.props.children}</div> + ); + } + +} + +/** + * Item component + */ +class TrayItem extends Component { + + static contextTypes = { + menu: PropTypes.object.isRequired + }; + + render() { + this.context.menu.append(new MenuItem(this.props)); + return null; + } + +} + +export { TrayMenu, TraySubmenu, TrayItem }; diff --git a/app/main.js b/app/main.js index 001a28c863..e923df1a8b 100644 --- a/app/main.js +++ b/app/main.js @@ -1,7 +1,6 @@ import path from 'path'; import url from 'url'; import {app, crashReporter, BrowserWindow, Menu} from 'electron'; -import trayMenu from './tray'; const isDevelopment = (process.env.NODE_ENV === 'development'); @@ -52,9 +51,6 @@ app.on('ready', async () => { show: false }); - // initialize tray menu - trayMenu.setup(); - mainWindow.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', diff --git a/app/tray.js b/app/tray.js deleted file mode 100644 index c93b207d0d..0000000000 --- a/app/tray.js +++ /dev/null @@ -1,24 +0,0 @@ -import path from 'path'; -import { app, Menu, Tray } from 'electron'; - -class TrayMenu { - - trayImpl = null; - - setup() { - const iconPath = path.join(__dirname, "assets/trayicon.png"); - this.trayImpl = new Tray(iconPath); - - const contextMenu = Menu.buildFromTemplate([ - {label: 'Item1', type: 'radio'}, - {label: 'Item2', type: 'radio'}, - {label: 'Item3', type: 'radio', checked: true}, - {label: 'Item4', type: 'radio'} - ]); - - this.trayImpl.setContextMenu(contextMenu); - } - -} - -module.exports = new TrayMenu(); |
