diff options
| author | elijah <elijah@riseup.net> | 2016-09-16 14:02:32 -0700 | 
|---|---|---|
| committer | Kali Kaneko (leap communications) <kali@leap.se> | 2016-09-22 11:40:11 -0400 | 
| commit | 073393af311d36c8ca7570ff0d3f0a3117c0b544 (patch) | |
| tree | e59286ac350ba17110392f53b6e48bcedfd12ef1 /ui/app/components/main_panel | |
| parent | ae5a20d059209f2027c05820dc3b4cfe7346c8a8 (diff) | |
[pkg] rename www to ui
Diffstat (limited to 'ui/app/components/main_panel')
| -rw-r--r-- | ui/app/components/main_panel/account_list.js | 113 | ||||
| -rw-r--r-- | ui/app/components/main_panel/email_section.js | 48 | ||||
| -rw-r--r-- | ui/app/components/main_panel/index.js | 80 | ||||
| -rw-r--r-- | ui/app/components/main_panel/main_panel.less | 212 | ||||
| -rw-r--r-- | ui/app/components/main_panel/section_layout.js | 59 | ||||
| -rw-r--r-- | ui/app/components/main_panel/user_section.js | 71 | ||||
| -rw-r--r-- | ui/app/components/main_panel/vpn_section.js | 0 | 
7 files changed, 583 insertions, 0 deletions
diff --git a/ui/app/components/main_panel/account_list.js b/ui/app/components/main_panel/account_list.js new file mode 100644 index 0000000..d0ef092 --- /dev/null +++ b/ui/app/components/main_panel/account_list.js @@ -0,0 +1,113 @@ +import React from 'react' +import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap' + +import App from 'app' +import Account from 'models/account' + +export default class AccountList extends React.Component { + +  static get defaultProps() {return{ +    account: null, +    accounts: [], +    onAdd: null, +    onRemove: null, +    onSelect: null +  }} + +  constructor(props) { +    super(props) + +    this.state = { +      mode: 'expanded' +    } + +    // prebind: +    this.select = this.select.bind(this) +    this.add    = this.add.bind(this) +    this.remove = this.remove.bind(this) +    this.expand = this.expand.bind(this) +    this.collapse = this.collapse.bind(this) +  } + +  select(e) { +    let account = this.props.accounts.find( +      account => account.id == e.currentTarget.dataset.id +    ) +    if (this.props.onSelect) { +      this.props.onSelect(account) +    } +  } + +  add() { +    App.show('wizard') +  } + +  remove() { +  } + +  expand() { +    this.setState({mode: 'expanded'}) +  } + +  collapse() { +    this.setState({mode: 'collapsed'}) +  } + +  render() { +    let style = {} +    let expandButton = null +    let plusminusButtons = null + +    if (this.state.mode == 'expanded') { +      expandButton = ( +        <Button onClick={this.collapse} className="expander btn-inverse btn-flat pull-right"> +          <Glyphicon glyph="triangle-left" /> +        </Button> +      ) +      plusminusButtons = ( +        <ButtonGroup style={style}> +          <Button onClick={this.add} className="btn-inverse"> +            <Glyphicon glyph="plus" /> +          </Button> +          <Button disabled={this.props.account == null} onClick={this.remove} className="btn-inverse"> +            <Glyphicon glyph="minus" /> +          </Button> +        </ButtonGroup> +      ) +    } else { +      style.width = '60px' +      expandButton = ( +        <Button onClick={this.expand} className="expander btn-inverse btn-flat pull-right"> +          <Glyphicon glyph="triangle-right" /> +        </Button> +      ) +    } + +    let items = this.props.accounts.map((account, i) => { +      let className = account == this.props.account ? 'active' : 'inactive' +      return ( +        <li key={i} className={className} onClick={this.select} data-id={account.id}> +          <span className="username">{account.userpart}</span> +          <span className="domain">{account.domain}</span> +          <span className="arc top"></span> +          <span className="arc bottom"></span> +        </li> +      ) +    }) + + +    return ( +      <div className="accounts" style={style}> +        <ul> +          {items} +        </ul> +        <ButtonToolbar> +          {plusminusButtons} +          {expandButton} +        </ButtonToolbar> +      </div> +    ) +  } + + +} diff --git a/ui/app/components/main_panel/email_section.js b/ui/app/components/main_panel/email_section.js new file mode 100644 index 0000000..a6525d9 --- /dev/null +++ b/ui/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/ui/app/components/main_panel/index.js b/ui/app/components/main_panel/index.js new file mode 100644 index 0000000..3cc6c11 --- /dev/null +++ b/ui/app/components/main_panel/index.js @@ -0,0 +1,80 @@ +// +// The main panel manages the current account and the list of available accounts +// +// It displays multiple sections, one for each service. +// + +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 { + +  static get defaultProps() {return{ +    initialAccount: null +  }} + +  constructor(props) { +    super(props) +    this.state = { +      account: null, +      accounts: [] +    } +    this.activateAccount = this.activateAccount.bind(this) +  } + +  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: Account.list +      }) +    } +  } + +  activateAccount(account) { +    this.setState({ +      account: 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/ui/app/components/main_panel/main_panel.less b/ui/app/components/main_panel/main_panel.less new file mode 100644 index 0000000..4e0ecb0 --- /dev/null +++ b/ui/app/components/main_panel/main_panel.less @@ -0,0 +1,212 @@ +// The space around account entries: +@accounts-padding: 8px; +@accounts-corner: 6px; +@accounts-width: 200px; + +// +// LAYOUT +// + +.main-panel { +  position: absolute; +  height: 100%; +  width: 100%; +  display: -webkit-flex; +  -webkit-flex-direction: row; + +  > .body { +    -webkit-flex: 1 1 auto; +    overflow: auto; +  } + +  .accounts { +    -webkit-flex: 0 0 auto; +    overflow-y: auto; +    overflow-x: hidden; + +    display: -webkit-flex; +    -webkit-flex-direction: column; +    ul { +      -webkit-flex: 1 1 1000px; +    } +    .btn-toolbar { +      -webkit-flex: 0 0 auto; +    } + +  } + +} + +// +// Style +// + + +.main-panel > .body { +  padding: 20px; +} + +.main-panel .accounts { +  background-color: #333; +  width: @accounts-width; +  padding: @accounts-padding; +  padding-right: 0px; +} + +.main-panel .accounts ul { +  list-style: none; +  margin: 0; +  padding: 0; +} + +.main-panel .accounts li { +  position: relative; +  cursor: pointer; +  color: white; +  padding: 15px; +  background-color: #444; +  margin-bottom: @accounts-padding; +  border-top-left-radius: @accounts-corner - 1; +  border-bottom-left-radius: @accounts-corner - 1; +  z-index: 100; +} + +.main-panel .accounts li span.domain { +  display: block; +  font-weight: bold; +  //margin-left: 40px; +} + +.main-panel .accounts li span.username { +  display: block; +  //margin-left: 40px; +  //line-height: 7px; +  //margin-bottom: 4px; +} + +/*.main-panel .accounts li span.icon { +  display: block; +  height: 32px; +  width: 32px; +  background-color: #999; +  float: left; +} +*/ + +.main-panel .accounts li.active { +  background-color: white; +  color: #333; +} + +.main-panel .accounts li.active span.arc { +  display: block; +  height: @accounts-corner; +  width: @accounts-corner; +  background-color: white; +  position: absolute; +  right: 0; +} + +.main-panel .accounts li.active span.arc.top { +  top: 0; +  margin-top: -@accounts-corner; +} +.main-panel .accounts li.active span.arc.bottom { +  bottom: 0; +  margin-bottom: -@accounts-corner; +} +.main-panel .accounts li.active span.arc:after { +  display: block; +  content: ""; +  border-radius: 100%; +  height: 0px; +  width: 0px; +  margin-left: -@accounts-corner; +} +.main-panel .accounts li.active span.arc.top:after { +  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-corner solid #333; +} + +.main-panel .accounts .btn.expander { +   margin-right: @accounts-padding; +} + +// +// SECTIONS +// + +@icon-size: 32px; +@status-size: 24px; +@section-padding: 10px; + +// service sections layout + +.main-panel .service-section { +  display: -webkit-flex; +  -webkit-flex-direction: row; +  > .icon { +    -webkit-flex: 0 0 auto; +  } +  > .body { +    -webkit-flex: 1 1 auto; +  } +  > .buttons { +    -webkit-flex: 0 0 auto; +  } +  > .status { +    -webkit-flex: 0 0 auto; +    display: -webkit-flex; +    -webkit-align-items: center; +  } + +} + +.main-panel .service-section div { +  //outline: 1px solid rgba(0,0,0,0.1); +} + +// service sections style + +.main-panel .service-section { +  background: #f6f6f6; +  border-radius: 4px; +  padding: 10px; +  margin-bottom: 10px; +  &.wide-margin { +    padding: 20px 20px 20px 10px; // arbitrary, looks nice +  } +  > .icon { +    padding-right: @section-padding; +    img { +      width: @icon-size; +      height: @icon-size; +    } +  } +  > .body { +    h1 { +      margin: 0; +      padding: 0; +      font-size: @icon-size - 10; +      line-height: @icon-size; +    } +  } +  > .buttons { +    padding-left: 10px; +  } +  > .status { +    padding-left: @section-padding; +    width: @section-padding + @status-size; +    img { +      width: @status-size; +      height: @status-size; +    } +  } +} + diff --git a/ui/app/components/main_panel/section_layout.js b/ui/app/components/main_panel/section_layout.js new file mode 100644 index 0000000..e7c6f2a --- /dev/null +++ b/ui/app/components/main_panel/section_layout.js @@ -0,0 +1,59 @@ +// +// This is the layout for a service section in the main window. +// It does not do anything except for arrange items using css and html. +// + +import React from 'react' + +export default class SectionLayout extends React.Component { + +  static get defaultProps() {return{ +    icon: null, +    buttons: null, +    status: null, +    className: "", +    style: {} +  }} + +  constructor(props) { +    super(props) +  } + +  render() { +    let className = ["service-section", this.props.className].join(' ') +    let status = null +    let icon = null +    let buttons = null + +    if (this.props.status) { +      status = ( +        <div className="status"> +          <img src={'img/' + this.props.status + '.svg' } /> +        </div> +      ) +    } +    if (this.props.icon) { +      icon = ( +        <div className="icon"> +          <img src={'img/' + this.props.icon + '.svg'} /> +        </div> +      ) +    } +    if (this.props.buttons) +      buttons = ( +        <div className="buttons"> +          {this.props.buttons} +        </div> +      ) +    return( +      <div className={className} style={this.props.style}> +        {icon} +        <div className="body"> +          {this.props.children} +        </div> +        {buttons} +        {status} +      </div> +    ) +  } +} diff --git a/ui/app/components/main_panel/user_section.js b/ui/app/components/main_panel/user_section.js new file mode 100644 index 0000000..0b4ba13 --- /dev/null +++ b/ui/app/components/main_panel/user_section.js @@ -0,0 +1,71 @@ +import React from 'react' +import { Button, Glyphicon, Alert } from 'react-bootstrap' +import SectionLayout from './section_layout' +import Login from 'components/login' +import Spinner from 'components/spinner' +import Account from 'models/account' + +import bitmask from 'lib/bitmask' + +export default class UserSection extends React.Component { + +  static get defaultProps() {return{ +    account: null, +    onLogout: null, +    onLogin: null +  }} + +  constructor(props) { +    super(props) +    this.state = { +      error: null, +      loading: false +    } +    this.logout = this.logout.bind(this) +  } + +  logout() { +    this.setState({loading: true}) +    this.props.account.logout().then( +      account => { +        this.setState({error: null, loading: false}) +        if (this.props.onLogout) { +          this.props.onLogout(account) +        } +      }, error => { +        this.setState({error: error, loading: false}) +      } +    ) +  } + +  render () { +    let message = null +    if (this.state.error) { +      // style may be: success, warning, danger, info +      message = ( +        <Alert bsStyle="danger">{this.state.error}</Alert> +      ) +    } + +    if (this.props.account.authenticated) { +      let button = null +      if (this.state.loading) { +        button = <Button disabled={true}><Spinner /></Button> +      } else { +        button = <Button onClick={this.logout}>Log Out</Button> +      } +      return ( +        <SectionLayout icon="user" buttons={button} status="on"> +          <h1>{this.props.account.address}</h1> +          {message} +        </SectionLayout> +      ) +    } else { +      return ( +        <SectionLayout icon="user" className="wide-margin"> +          <Login onLogin={this.props.onLogin} domain={this.props.account.domain} /> +        </SectionLayout> +      ) +    } +  } +} diff --git a/ui/app/components/main_panel/vpn_section.js b/ui/app/components/main_panel/vpn_section.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ui/app/components/main_panel/vpn_section.js  | 
