summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--www/README.md2
-rw-r--r--www/app/components/center.js23
-rw-r--r--www/app/components/login.js2
-rw-r--r--www/app/components/main_panel/email_section.js48
-rw-r--r--www/app/components/main_panel/index.js27
-rw-r--r--www/app/components/main_panel/main_panel.less60
-rw-r--r--www/app/components/panel_switcher.js4
-rw-r--r--www/app/components/spinner/spinner.css20
-rw-r--r--www/app/components/wizard/wizard.less8
-rw-r--r--www/app/css/common.css23
-rw-r--r--www/app/lib/bitmask.js3
-rw-r--r--www/app/main.js11
-rw-r--r--www/app/models/account.js67
-rw-r--r--www/app/models/dummy_account.js33
-rw-r--r--www/package.json1
-rw-r--r--www/webpack.config.js4
16 files changed, 271 insertions, 65 deletions
diff --git a/www/README.md b/www/README.md
index f1d7c34..17c5042 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 a3b6409..6fa6212 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 4f7b628..fe4ef5b 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 e69de29..a6525d9 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 910d58b..3cc6c11 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 3172bba..4e0ecb0 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 f4f32cf..aaf2dc5 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 4e7f3ee..5e8535c 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 3336ffb..29efc20 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 4b48010..acf164e 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 03b6c7b..af019b1 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 0d712af..b162895 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 367961b..fa3b981 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 0000000..bf0391b
--- /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 128a9e7..d491edd 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 7b46e1b..a29e575 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' }
])
],