summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2017-04-19 19:51:50 -0700
committerKali Kaneko (leap communications) <kali@leap.se>2017-04-20 18:08:32 +0200
commit3810cfb433308dd9ced9c2185ecbb5f206e07f4c (patch)
tree73aceadb032cdfa9ba5c81944c05bc8e42cdc385 /ui
parent73836440b099f10174bc0b79ad4b8c85fcb9caf1 (diff)
[feat] added app buttons (quit & about bitmask)
- Closes #8803
Diffstat (limited to 'ui')
-rw-r--r--ui/app/components/bye_splash.js99
-rw-r--r--ui/app/components/greeter_panel.js17
-rw-r--r--ui/app/components/main_panel/account_list.js2
-rw-r--r--ui/app/components/main_panel/app_buttons.js123
-rw-r--r--ui/app/components/main_panel/main_panel.less4
-rw-r--r--ui/app/components/panel_switcher.js2
-rw-r--r--ui/app/components/splash.js62
7 files changed, 285 insertions, 24 deletions
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 (
+ <div style={backgroundStyle}>
+ <style>{style}</style>
+ <Center>
+ <img className="mask" src='img/mask.svg' width='200px' />
+ <div className={messageClass} style={foregroundStyle}>
+ {message}
+ </div>
+ </Center>
+ </div>
+ )
+ }
+} \ 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 <div>
<Splash speed="slow" mask={false} />
@@ -29,9 +33,14 @@ export default class GreeterPanel extends React.Component {
rememberAllowed={false} autoAllowed={true} />
</Area>
<Area position="bottom" type="dark" className="greeter">
- <Glyphicon glyph="user" />
- &nbsp;
- <a href="javascript:void(0)" onClick={this.newAccount.bind(this)}>Create a new account...</a>
+ <ButtonToolbar>
+ <Button onClick={this.quit.bind(this)} className="pull-right">
+ <Glyphicon glyph="off" />
+ </Button>
+ <Button onClick={this.newAccount.bind(this)}>
+ <Glyphicon glyph="user" /> New account...
+ </Button>
+ </ButtonToolbar>
</Area>
</Center>
</div>
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 (
<div className="accounts" style={style}>
+ <AppButtons />
<ul>
{items}
</ul>
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 = (
+ <Button onClick={this.quit} className="btn-inverse">
+ <Glyphicon glyph="off" />
+ </Button>
+ )
+
+ aboutButton = (
+ <Button onClick={this.showAbout} className="btn-inverse">
+ <Glyphicon glyph="info-sign" />
+ </Button>
+ )
+
+ if (this.state.showAbout) {
+ aboutModal = (
+ <Modal show={true} onHide={this.hideAbout}>
+ <Splash speed="fast" mask={true} fullscreen={false} />
+ <Modal.Body>
+ <h2><b>Bitmask</b></h2>
+ <p>
+ Bitmask Desktop Client, Version {this.state.version}
+ </p>
+ <p>
+ Bitmask is Free Software, released under the GNU General Public License, version 3.<br/>
+ Copyright 2017 LEAP Encryption Access Project.
+ </p>
+ <p>
+ 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.
+ </p>
+ <ButtonToolbar>
+ <Button onClick={this.hideAbout}>
+ <Glyphicon glyph="remove" /> Close
+ </Button>
+ <Button onClick={this.openBugReport}>
+ <Glyphicon glyph="alert" /> Report Bug
+ </Button>
+ <Button onClick={this.openDonate}>
+ <Glyphicon glyph="heart" /> Donate
+ </Button>
+ <Button onClick={this.openCode}>
+ <Glyphicon glyph="circle-arrow-down" /> Clone Source
+ </Button>
+ </ButtonToolbar>
+ </Modal.Body>
+ </Modal>
+ )
+ }
+
+ return (
+ <ButtonToolbar className="app-buttons">
+ {quitButton}
+ {aboutButton}
+ {aboutModal}
+ </ButtonToolbar>
+ )
+ }
+
+
+}
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
+ )
+ }
}
}