From 3810cfb433308dd9ced9c2185ecbb5f206e07f4c Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 19 Apr 2017 19:51:50 -0700 Subject: [feat] added app buttons (quit & about bitmask) - Closes #8803 --- ui/app/components/bye_splash.js | 99 +++++++++++++++++++++ ui/app/components/greeter_panel.js | 17 +++- ui/app/components/main_panel/account_list.js | 2 + ui/app/components/main_panel/app_buttons.js | 123 +++++++++++++++++++++++++++ ui/app/components/main_panel/main_panel.less | 4 + ui/app/components/panel_switcher.js | 2 + ui/app/components/splash.js | 62 +++++++++----- 7 files changed, 285 insertions(+), 24 deletions(-) create mode 100644 ui/app/components/bye_splash.js create mode 100644 ui/app/components/main_panel/app_buttons.js (limited to 'ui') diff --git a/ui/app/components/bye_splash.js b/ui/app/components/bye_splash.js new file mode 100644 index 00000000..633aa3d3 --- /dev/null +++ b/ui/app/components/bye_splash.js @@ -0,0 +1,99 @@ +/* + * a parting window + */ + +import React from 'react' +import Center from 'components/center' +import bitmask from 'lib/bitmask' + +export default class ByeSplash extends React.Component { + + static get defaultProps() {return{ + delay: 1000 + }} + + constructor(props) { + super(props) + this.state = { + errorMessage: null, + readyToQuit: false + } + this.tick = this.tick.bind(this) + } + + componentDidMount() { + this.interval = setInterval(this.tick, this.props.delay) + bitmask.core.stop().then(msg => { + console.log(msg) + this.setState({readyToQuit: true}) + }, error => { + console.log(error) + this.setState({errorMessage: error}) + }) + } + + componentWillUnmount() { + clearInterval(this.interval) + } + + tick() { + if (this.state.readyToQuit) { + if (typeof(bitmaskApp) == 'undefined') { + window.close() + } else { + bitmaskApp.shutdown() + } + } else { + clearInterval(this.interval) + this.interval = setInterval(this.tick, 200) + } + } + + render () { + let message = null + let messageClass = "ok" + if (this.state.errorMessage) { + messageClass = "error" + message = this.state.errorMessage + } + + let backgroundStyle = { + position: "absolute", + width: "100%", + height: "100%", + backgroundColor: "#333" + } + let foregroundStyle = { + marginTop: "10px", + color: "#999", + textAlign: "center" + } + let style = ` + .mask, .ok { + animation: fadeout 2s infinite ease-in-out; + -webkit-animation: fadeout 2s infinite ease-in-out; + } + @keyframes fadeout { + 0% {opacity: 1} + 50% {opacity: 0} + 100% {opacity: 1} + } + @-webkit-keyframes fadeout { + 0% {opacity: 1} + 50% {opacity: 0} + 100% {opacity: 1} + } + ` + return ( +
+ +
+ +
+ {message} +
+
+
+ ) + } +} \ No newline at end of file diff --git a/ui/app/components/greeter_panel.js b/ui/app/components/greeter_panel.js index 31403573..a2783ec2 100644 --- a/ui/app/components/greeter_panel.js +++ b/ui/app/components/greeter_panel.js @@ -3,7 +3,7 @@ import Login from './login' import Center from './center' import Splash from './splash' import Area from './area' -import { Glyphicon } from 'react-bootstrap' +import { Glyphicon, Button, ButtonToolbar } from 'react-bootstrap' import App from 'app' export default class GreeterPanel extends React.Component { @@ -20,6 +20,10 @@ export default class GreeterPanel extends React.Component { App.start() } + quit() { + App.show('bye_splash') + } + render () { return
@@ -29,9 +33,14 @@ export default class GreeterPanel extends React.Component { rememberAllowed={false} autoAllowed={true} /> - -   - Create a new account... + + + +
diff --git a/ui/app/components/main_panel/account_list.js b/ui/app/components/main_panel/account_list.js index 85d6824d..862f7e35 100644 --- a/ui/app/components/main_panel/account_list.js +++ b/ui/app/components/main_panel/account_list.js @@ -4,6 +4,7 @@ import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap' import App from 'app' import Account from 'models/account' import Confirmation from 'components/confirmation' +import AppButtons from './app_buttons' export default class AccountList extends React.Component { @@ -124,6 +125,7 @@ export default class AccountList extends React.Component { return (
+
    {items}
diff --git a/ui/app/components/main_panel/app_buttons.js b/ui/app/components/main_panel/app_buttons.js new file mode 100644 index 00000000..cda97b1c --- /dev/null +++ b/ui/app/components/main_panel/app_buttons.js @@ -0,0 +1,123 @@ +import React from 'react' +import {Modal, Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap' + +import App from 'app' +import Splash from 'components/splash' +import bitmask from 'lib/bitmask' + +export default class AppButtons extends React.Component { + + static get defaultProps() {return{ + }} + + constructor(props) { + super(props) + + this.state = { + showAbout: false, + version: null + } + + // prebind: + this.showAbout = this.showAbout.bind(this) + this.hideAbout = this.hideAbout.bind(this) + this.quit = this.quit.bind(this) + this.openBugReport = this.openBugReport.bind(this) + this.openDonate = this.openDonate.bind(this) + this.openCode = this.openCode.bind(this) + } + + showAbout() { + bitmask.core.version().then(result => { + this.setState({version: result.version_core}) + }) + this.setState({showAbout: true}) + } + + hideAbout() { + this.setState({showAbout: false}) + } + + quit() { + App.show('bye_splash') + } + + openURL(url) { + if (typeof(bitmaskApp) == 'undefined') { + window.open(url) + } else { + bitmaskApp.openSystemBrowser(url) + } + } + + openBugReport() { this.openURL('https://0xacab.org/leap/bitmask-dev') } + openDonate() { this.openURL('https://leap.se/donate') } + openCode() { this.openURL('https://leap.se/source') } + + render() { + let style = {} + let quitButton = null + let aboutButton = null + let aboutModal = null + + quitButton = ( + + ) + + aboutButton = ( + + ) + + if (this.state.showAbout) { + aboutModal = ( + + + +

Bitmask

+

+ Bitmask Desktop Client, Version {this.state.version} +

+

+ Bitmask is Free Software, released under the GNU General Public License, version 3.
+ Copyright 2017 LEAP Encryption Access Project. +

+

+ The Bitmask application is lovingly hand-crafted by developers + all over the world. Development is principally sponsored by the + LEAP Encryption Access Project, an organization dedicated to + defending democracy by protecting the right to whisper. +

+ + + + + + +
+
+ ) + } + + return ( + + {quitButton} + {aboutButton} + {aboutModal} + + ) + } + + +} diff --git a/ui/app/components/main_panel/main_panel.less b/ui/app/components/main_panel/main_panel.less index 10399bdc..f15ecedc 100644 --- a/ui/app/components/main_panel/main_panel.less +++ b/ui/app/components/main_panel/main_panel.less @@ -141,6 +141,10 @@ margin-right: @accounts-padding; } +.main-panel .accounts .app-buttons { + margin-bottom: @accounts-padding; +} + // // SECTIONS // diff --git a/ui/app/components/panel_switcher.js b/ui/app/components/panel_switcher.js index 9a06b49f..e476487c 100644 --- a/ui/app/components/panel_switcher.js +++ b/ui/app/components/panel_switcher.js @@ -8,6 +8,7 @@ import GreeterPanel from './greeter_panel' import MainPanel from './main_panel' import Wizard from './wizard' import Addressbook from './addressbook' +import ByeSplash from './bye_splash' import App from 'app' import 'lib/common' @@ -73,5 +74,6 @@ export default class PanelSwitcher extends React.Component { render_main(props) {return elem(MainPanel, props)} render_addressbook(props) {return elem(Addressbook, props)} render_error(props) {return elem(ErrorPanel, props)} + render_bye_splash(props) {return elem(ByeSplash, props)} } diff --git a/ui/app/components/splash.js b/ui/app/components/splash.js index 46170d23..54dfb39a 100644 --- a/ui/app/components/splash.js +++ b/ui/app/components/splash.js @@ -16,7 +16,9 @@ export default class Splash extends React.Component { static get defaultProps() {return{ speed: "fast", - mask: true, + mask: false, + fullscreen: true, + style: null, onClick: null }} @@ -29,10 +31,10 @@ export default class Splash extends React.Component { this.resize = this.resize.bind(this) this.click = this.click.bind(this) if (this.props.speed == "fast") { - this.fps = 30 + this.fps = 20 this.stepAngle = 0.005 } else { - this.fps = 30 + this.fps = 20 this.stepAngle = 0.0005 } } @@ -61,9 +63,11 @@ export default class Splash extends React.Component { } resize() { - this.canvas.width = window.innerWidth - this.canvas.height = window.innerHeight - this.updateCanvas() + if (this.props.fullscreen) { + this.canvas.width = window.innerWidth + this.canvas.height = window.innerHeight + this.updateCanvas() + } } updateCanvas() { @@ -71,7 +75,7 @@ export default class Splash extends React.Component { const arcAngle = 1 / arcCount const x = this.canvas.width / 2 const y = this.canvas.height / 2 - const radius = screen.height + screen.width + const radius = this.canvas.height + this.canvas.width for (let i = 0; i < arcCount; i++) { let startAngle = Math.PI * 2 * i/arcCount + this.stepAngle*this.counter @@ -107,25 +111,43 @@ export default class Splash extends React.Component { mask = React.DOM.img({ src: 'img/mask.svg', style: { + width: '100px', position: 'absolute', left: '50%', top: '50%', - marginLeft: -330/2 + 'px', - marginTop: -174/2 + 'px', + marginLeft: '-50px', + marginTop: '-28px' } }) } - return React.DOM.div( - {style: {overflow: 'hidden'}}, - React.DOM.canvas({ - ref: 'canvas', - style: {position: 'absolute'}, - width: window.innerWidth, - height: window.innerHeight, - }), - mask, - overlay - ) + if (this.props.fullscreen) { + return React.DOM.div( + {style: {overflow: 'hidden'}}, + React.DOM.canvas({ + ref: 'canvas', + style: {position: 'absolute'}, + width: window.innerWidth, + height: window.innerHeight, + }), + mask, + overlay + ) + } else { + return React.DOM.div( + {style: {position: 'relative'}}, + React.DOM.canvas({ + style: { + width: '100%', + borderTopLeftRadius: '4px', + borderTopRightRadius: '4px', + }, + ref: 'canvas', + width: '1000px', + height: '200px' + }), + mask + ) + } } } -- cgit v1.2.3