diff options
-rw-r--r-- | www/README.md | 2 | ||||
-rw-r--r-- | www/app/components/center.js | 23 | ||||
-rw-r--r-- | www/app/components/login.js | 2 | ||||
-rw-r--r-- | www/app/components/main_panel/email_section.js | 48 | ||||
-rw-r--r-- | www/app/components/main_panel/index.js | 27 | ||||
-rw-r--r-- | www/app/components/main_panel/main_panel.less | 60 | ||||
-rw-r--r-- | www/app/components/panel_switcher.js | 4 | ||||
-rw-r--r-- | www/app/components/spinner/spinner.css | 20 | ||||
-rw-r--r-- | www/app/components/wizard/wizard.less | 8 | ||||
-rw-r--r-- | www/app/css/common.css | 23 | ||||
-rw-r--r-- | www/app/lib/bitmask.js | 3 | ||||
-rw-r--r-- | www/app/main.js | 11 | ||||
-rw-r--r-- | www/app/models/account.js | 67 | ||||
-rw-r--r-- | www/app/models/dummy_account.js | 33 | ||||
-rw-r--r-- | www/package.json | 1 | ||||
-rw-r--r-- | www/webpack.config.js | 4 |
16 files changed, 271 insertions, 65 deletions
diff --git a/www/README.md b/www/README.md index f1d7c34f..17c50424 100644 --- a/www/README.md +++ b/www/README.md @@ -64,6 +64,8 @@ way. We have enabled these plugins: * babel-presets-stage-0: Allows the use of some ES7 proposals, even though these are not standardized yet. Makes classes nicer. +* babel-polyfill: This is not part of the babel transpiling, but is distributed by babel. This polyfill will give you a full ES2015 environment even if the browser is missing some javascript features. We include this in the 'entry' option of the webpack config. https://babeljs.io/docs/usage/polyfill/ + **react** React is an efficient way to generate HTML views with Javascript. It allows you diff --git a/www/app/components/center.js b/www/app/components/center.js index a3b6409a..6fa62128 100644 --- a/www/app/components/center.js +++ b/www/app/components/center.js @@ -4,22 +4,6 @@ import React from 'react' -const CONTAINER_CSS = { - position: 'absolute', - display: 'flex', - justifyContent: 'center', - alignContent: 'center', - alignItems: 'center', - top: "0px", - left: "0px", - height: "100%", - width: "100%" -} - -const ITEM_CSS = { - flex: "0 1 auto" -} - class Center extends React.Component { static get defaultProps() {return{ @@ -31,9 +15,12 @@ class Center extends React.Component { } render() { - let style = this.props.width ? Object.assign({width: this.props.width + 'px'}, ITEM_CSS) : ITEM_CSS + let style = null + if (this.props.width) { + style = {width: this.props.width + 'px'} + } return ( - <div className="center-container" style={CONTAINER_CSS}> + <div className="center-container"> <div className="center-item" style={style}> {this.props.children} </div> diff --git a/www/app/components/login.js b/www/app/components/login.js index 4f7b6289..fe4ef5b2 100644 --- a/www/app/components/login.js +++ b/www/app/components/login.js @@ -274,7 +274,7 @@ class Login extends React.Component { if (!this.maySubmit()) { return } this.setState({loading: true}) - let account = new Account(this.state.username) + let account = Account.find(this.state.username) account.login(this.state.password).then( account => { this.setState({loading: false}) diff --git a/www/app/components/main_panel/email_section.js b/www/app/components/main_panel/email_section.js index e69de29b..a6525d92 100644 --- a/www/app/components/main_panel/email_section.js +++ b/www/app/components/main_panel/email_section.js @@ -0,0 +1,48 @@ +import React from 'react' +//import { Button, Glyphicon, Alert } from 'react-bootstrap' +import SectionLayout from './section_layout' +import Account from 'models/account' +import Spinner from 'components/spinner' +import bitmask from 'lib/bitmask' + +export default class EmailSection extends React.Component { + + static get defaultProps() {return{ + account: null + }} + + constructor(props) { + super(props) + this.state = { + status: null + } + this.openKeys = this.openKeys.bind(this) + this.openApp = this.openApp.bind(this) + this.openPrefs = this.openPrefs.bind(this) + + console.log('email constructor') + } + + openKeys() {} + openApp() {} + openPrefs() {} + + render () { + //let message = null + //if (this.state.error) { + // // style may be: success, warning, danger, info + // message = ( + // <Alert bsStyle="danger">{this.state.error}</Alert> + // ) + //} + let button = null + if (this.state.status == 'ready') { + button = <Button onClick={this.openApp}>Open Email</Button> + } + return ( + <SectionLayout icon="envelope" status="on" button={button}> + <h1>inbox: </h1> + </SectionLayout> + ) + } +} diff --git a/www/app/components/main_panel/index.js b/www/app/components/main_panel/index.js index 910d58b6..3cc6c11f 100644 --- a/www/app/components/main_panel/index.js +++ b/www/app/components/main_panel/index.js @@ -7,10 +7,13 @@ import React from 'react' import App from 'app' import Login from 'components/login' +import Account from 'models/account' +import DummyAccount from 'models/dummy_account' import './main_panel.less' import AccountList from './account_list' import UserSection from './user_section' +import EmailSection from './email_section' export default class MainPanel extends React.Component { @@ -29,9 +32,12 @@ export default class MainPanel extends React.Component { componentWillMount() { if (this.props.initialAccount) { + console.log(Account.list) + Account.add(this.props.initialAccount) + Account.add(new DummyAccount(this.props.initialAccount)) this.setState({ account: this.props.initialAccount, - accounts: [this.props.initialAccount] + accounts: Account.list }) } } @@ -39,16 +45,33 @@ export default class MainPanel extends React.Component { activateAccount(account) { this.setState({ account: account, - accounts: [account] + accounts: Account.list }) } + //setAccounts(accounts) { + // this.setState({ + // accounts: accounts + // }) + //} + render() { + let emailSection = null + let vpnSection = null + + if (this.state.account.authenticated) { + if (this.state.account.hasEmail) { + emailSection = <EmailSection account={this.state.account} /> + } + } + return ( <div className="main-panel"> <AccountList account={this.state.account} accounts={this.state.accounts} onSelect={this.activateAccount} /> <div className="body"> <UserSection account={this.state.account} onLogin={this.activateAccount} onLogout={this.activateAccount}/> + {vpnSection} + {emailSection} </div> </div> ) diff --git a/www/app/components/main_panel/main_panel.less b/www/app/components/main_panel/main_panel.less index 3172bba5..4e0ecb05 100644 --- a/www/app/components/main_panel/main_panel.less +++ b/www/app/components/main_panel/main_panel.less @@ -1,5 +1,6 @@ // The space around account entries: @accounts-padding: 8px; +@accounts-corner: 6px; @accounts-width: 200px; // @@ -10,26 +11,26 @@ position: absolute; height: 100%; width: 100%; - display: flex; - flex-direction: row; + display: -webkit-flex; + -webkit-flex-direction: row; > .body { - flex: 1 1 auto; + -webkit-flex: 1 1 auto; overflow: auto; } .accounts { - flex: 0 0 auto; + -webkit-flex: 0 0 auto; overflow-y: auto; overflow-x: hidden; - display: flex; - flex-direction: column; + display: -webkit-flex; + -webkit-flex-direction: column; ul { - flex: 1 1 1000px; + -webkit-flex: 1 1 1000px; } .btn-toolbar { - flex: 0 0 auto; + -webkit-flex: 0 0 auto; } } @@ -40,6 +41,7 @@ // Style // + .main-panel > .body { padding: 20px; } @@ -64,8 +66,8 @@ padding: 15px; background-color: #444; margin-bottom: @accounts-padding; - border-top-left-radius: @accounts-padding; - border-bottom-left-radius: @accounts-padding; + border-top-left-radius: @accounts-corner - 1; + border-bottom-left-radius: @accounts-corner - 1; z-index: 100; } @@ -98,8 +100,8 @@ .main-panel .accounts li.active span.arc { display: block; - height: @accounts-padding; - width: @accounts-padding; + height: @accounts-corner; + width: @accounts-corner; background-color: white; position: absolute; right: 0; @@ -107,11 +109,11 @@ .main-panel .accounts li.active span.arc.top { top: 0; - margin-top: -@accounts-padding; + margin-top: -@accounts-corner; } .main-panel .accounts li.active span.arc.bottom { bottom: 0; - margin-bottom: -@accounts-padding; + margin-bottom: -@accounts-corner; } .main-panel .accounts li.active span.arc:after { display: block; @@ -119,23 +121,23 @@ border-radius: 100%; height: 0px; width: 0px; - margin-left: -@accounts-padding; + margin-left: -@accounts-corner; } .main-panel .accounts li.active span.arc.top:after { - border: @accounts-padding solid transparent; - border-right: @accounts-padding solid #333; - margin-top: -@accounts-padding; + border: @accounts-corner solid transparent; + border-right: @accounts-corner solid #333; + margin-top: -@accounts-corner; + -webkit-transform: rotate(45deg); transform: rotate(45deg); } .main-panel .accounts li.active span.arc.bottom:after { - border: @accounts-padding solid #333; + border: @accounts-corner solid #333; } .main-panel .accounts .btn.expander { margin-right: @accounts-padding; } - // // SECTIONS // @@ -147,22 +149,23 @@ // service sections layout .main-panel .service-section { - display: flex; - flex-direction: row; + display: -webkit-flex; + -webkit-flex-direction: row; > .icon { - flex: 0 0 auto; + -webkit-flex: 0 0 auto; } > .body { - flex: 1 1 auto; + -webkit-flex: 1 1 auto; } > .buttons { - flex: 0 0 auto; + -webkit-flex: 0 0 auto; } > .status { - flex: 0 0 auto; - display: flex; - align-items: center; + -webkit-flex: 0 0 auto; + display: -webkit-flex; + -webkit-align-items: center; } + } .main-panel .service-section div { @@ -175,6 +178,7 @@ background: #f6f6f6; border-radius: 4px; padding: 10px; + margin-bottom: 10px; &.wide-margin { padding: 20px 20px 20px 10px; // arbitrary, looks nice } diff --git a/www/app/components/panel_switcher.js b/www/app/components/panel_switcher.js index f4f32cf6..aaf2dc5b 100644 --- a/www/app/components/panel_switcher.js +++ b/www/app/components/panel_switcher.js @@ -53,4 +53,6 @@ export default class PanelSwitcher extends React.Component { render_splash(props) {return elem(Splash, props)} render_wizard(props) {return elem(Wizard, props)} render_greeter(props) {return elem(GreeterPanel, props)} - render_main(props) {return elem(MainPanel, props)}} + render_main(props) {return elem(MainPanel, props)} + +} diff --git a/www/app/components/spinner/spinner.css b/www/app/components/spinner/spinner.css index 4e7f3ee8..5e8535c9 100644 --- a/www/app/components/spinner/spinner.css +++ b/www/app/components/spinner/spinner.css @@ -10,12 +10,28 @@ vertical-align: middle; border-radius: 100%; display: inline-block; + -webkit-animation: bouncedelay 1.5s infinite ease-in-out; animation: bouncedelay 1.5s infinite ease-in-out; + -webkit-animation-fill-mode: both; animation-fill-mode: both; } -.spinner .spin1 { animation-delay: -.46s } -.spinner .spin2 { animation-delay: -.24s } +.spinner .spin1 { + -webkit-animation-delay: -.46s; + animation-delay: -.46s; +} +.spinner .spin2 { + -webkit-animation-delay: -.24s; + animation-delay: -.24s; +} + +@-webkit-keyframes bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0.5); + } 40% { + -webkit-transform: scale(0.9); + } +} @keyframes bouncedelay { 0%, 80%, 100% { diff --git a/www/app/components/wizard/wizard.less b/www/app/components/wizard/wizard.less index 3336ffb8..29efc20e 100644 --- a/www/app/components/wizard/wizard.less +++ b/www/app/components/wizard/wizard.less @@ -3,12 +3,16 @@ height: 100%; width: 100%; + display: -webkit-flex; display: flex; + -webkit-flex-direction: column; flex-direction: column; + -webkit-flex: 1; flex: 1; } .wizard .stage .footer { + -webkit-flex: 0 0 auto; flex: 0 0 auto; background-color: #ddd; padding: 20px; @@ -16,6 +20,7 @@ } .wizard .stage .header { + -webkit-flex: 0 0 auto; flex: 0 0 auto; padding: 20px; background-color: #333; @@ -28,9 +33,12 @@ } .wizard .stage .body { + -webkit-flex: 1 1 auto; flex: 1 1 auto; padding: 20px; overflow: auto; + display: -webkit-flex; display: flex; + -webkit-flex-direction: column; flex-direction: column; }
\ No newline at end of file diff --git a/www/app/css/common.css b/www/app/css/common.css index 4b48010a..acf164ee 100644 --- a/www/app/css/common.css +++ b/www/app/css/common.css @@ -56,4 +56,25 @@ body { /*.btn.btn-default { background-color: #eee !important; } -*/
\ No newline at end of file +*/ + +/* + * center component + */ + +.center-container { + position: absolute; + display: -webkit-flex; + -webkit-flex-flow: row nowrap; + -webkit-justify-content: center; + -webkit-align-content: center; + -webkit-align-items: center; + top: 0px; + left: 0px; + height: 100%; + width: 100%; +} + +.center-container .center-item { + -webkit-flex: 0 1 auto; +} diff --git a/www/app/lib/bitmask.js b/www/app/lib/bitmask.js index 03b6c7bf..af019b12 100644 --- a/www/app/lib/bitmask.js +++ b/www/app/lib/bitmask.js @@ -27,6 +27,9 @@ * finished or will fail if there was any error. Errors are always user readable * strings. */ + +import Promise from 'promise' + var bitmask = function(){ var event_handlers = {}; diff --git a/www/app/main.js b/www/app/main.js index 0d712af6..b1628953 100644 --- a/www/app/main.js +++ b/www/app/main.js @@ -1,3 +1,9 @@ +// +// main entry point for app execution +// +// This is determined by the 'entry' option in webpack.config.js +// + import React from 'react' import ReactDOM from 'react-dom' @@ -9,9 +15,6 @@ class Main extends React.Component { return React.createElement(PanelSwitcher) } - // - // main entry point for app execution - // componentDidMount() { App.start() } @@ -20,4 +23,4 @@ class Main extends React.Component { ReactDOM.render( React.createElement(Main), document.getElementById('app') -)
\ No newline at end of file +) diff --git a/www/app/models/account.js b/www/app/models/account.js index 367961bf..fa3b9813 100644 --- a/www/app/models/account.js +++ b/www/app/models/account.js @@ -8,11 +8,7 @@ import bitmask from 'lib/bitmask' export default class Account { constructor(address, props={}) { - if (!address.match('@')) { - this._address = '@' + address - } else { - this._address = address - } + this.address = address this._authenticated = props.authenticated } @@ -34,6 +30,14 @@ export default class Account { return this._address } + set address(address) { + if (!address.match('@')) { + this._address = '@' + address + } else { + this._address = address + } + } + get userpart() { return this._address.split('@')[0] } @@ -42,6 +46,10 @@ export default class Account { return this._authenticated } + get hasEmail() { + return true + } + // // returns a promise, fulfill is passed account object // @@ -71,6 +79,33 @@ export default class Account { } // + // returns the matching account in the list of accounts, or adds it + // if it is not already present. + // + static find(address) { + // search by full address + let account = Account.list.find(i => { + return i.address == address + }) + // failing that, search by domain + if (!account) { + let domain = '@' + address.split('@')[1] + account = Account.list.find(i => { + return i.address == domain + }) + if (account) { + account.address = address + } + } + // failing that, create new account + if (!account) { + account = new Account(address) + Account.list.push(account) + } + return account + } + + // // returns a promise, fullfill is passed account object // static active() { @@ -85,4 +120,24 @@ export default class Account { ) } -}
\ No newline at end of file + static add(account) { + if (!Account.list.find(i => {return i.id == account.id})) { + Account.list.push(account) + } + } + + static remove(account) { + Account.list = Account.list.filter(i => { + return i.id != account.id + }) + } + // return Account.list + // return new Promise(function(resolve, reject) { + // window.setTimeout(function() { + // resolve(['@blah', '@lala']) + // }, 1000) + // }) + // } +} + +Account.list = []
\ No newline at end of file diff --git a/www/app/models/dummy_account.js b/www/app/models/dummy_account.js new file mode 100644 index 00000000..bf0391be --- /dev/null +++ b/www/app/models/dummy_account.js @@ -0,0 +1,33 @@ +// +// A proxy of an account, but with a different ID. For testing. +// + +import bitmask from 'lib/bitmask' + +export default class DummyAccount { + + constructor(account) { + this.account = account + } + + get id() { + return 'dummy--' + this.account.address + } + + get domain() {return this.account.domain} + get address() {return this.account.address} + get userpart() {return this.account.userpart} + get authenticated() {return this.account.authenticated} + get hasEmail() {return this.account.hasEmail} + login(password) {return this.account.login(password)} + + logout() { + return bitmask.user.logout(this.address).then( + response => { + this._authenticated = false + this._address = '@' + this.domain + return this + } + ) + } +} diff --git a/www/package.json b/www/package.json index 128a9e71..d491edd8 100644 --- a/www/package.json +++ b/www/package.json @@ -9,6 +9,7 @@ "devDependencies": { "babel": "^6.5.2", "babel-loader": "^6.2.4", + "babel-polyfill": "^6.13.0", "babel-preset-es2015": "^6.9.0", "babel-preset-react": "^6.11.1", "babel-preset-stage-0": "^6.5.0", diff --git a/www/webpack.config.js b/www/webpack.config.js index 7b46e1b1..a29e5753 100644 --- a/www/webpack.config.js +++ b/www/webpack.config.js @@ -4,7 +4,7 @@ var CopyWebpackPlugin = require('copy-webpack-plugin'); var config = { context: path.join(__dirname, 'app'), - entry: './main.js', + entry: ['babel-polyfill', './main.js'], output: { path: path.join(__dirname, 'public'), filename: 'app.bundle.js' @@ -47,7 +47,7 @@ var config = { { from: 'img/*'}, { from: 'index.html' }, { from: '../node_modules/bootstrap/dist/css/bootstrap.min.css', to: 'css' }, - { from: '../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2', to: 'fonts' }, + { from: '../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff', to: 'fonts' }, { from: '../node_modules/zxcvbn/dist/zxcvbn.js', to: 'js' } ]) ], |