diff options
Diffstat (limited to 'ui/app')
| -rw-r--r-- | ui/app/components/main_panel/email_section.js | 97 | ||||
| -rw-r--r-- | ui/app/components/main_panel/imap_button.js | 121 | ||||
| -rw-r--r-- | ui/app/components/main_panel/main_panel.less | 1 | ||||
| -rw-r--r-- | ui/app/css/common.css | 28 | ||||
| -rw-r--r-- | ui/app/img/error.svg | 107 | 
5 files changed, 346 insertions, 8 deletions
| diff --git a/ui/app/components/main_panel/email_section.js b/ui/app/components/main_panel/email_section.js index a3ff11c..2bb5865 100644 --- a/ui/app/components/main_panel/email_section.js +++ b/ui/app/components/main_panel/email_section.js @@ -1,10 +1,57 @@  import React from 'react' -//import { Button, Glyphicon, Alert } from 'react-bootstrap' +import { Button, Glyphicon, Alert } from 'react-bootstrap' +  import SectionLayout from './section_layout' +import IMAPButton from './imap_button' +  import Account from 'models/account'  import Spinner from 'components/spinner'  import bitmask from 'lib/bitmask' +const GENERAL_NOTICES = [ +  "KEYMANAGER_KEY_FOUND",  // (address) +  "KEYMANAGER_KEY_NOT_FOUND",  // (address) +  "KEYMANAGER_LOOKING_FOR_KEY",  // (address) +  "KEYMANAGER_DONE_UPLOADING_KEYS",  // (address) + +  "SMTP_START_ENCRYPT_AND_SIGN",  // (from_addr) +  "SMTP_END_ENCRYPT_AND_SIGN",  // (from_addr) +  "SMTP_START_SIGN",  // (from_addr) +  "SMTP_END_SIGN",  // (from_addr) +  "SMTP_SEND_MESSAGE_START",  // (from_addr) +  "SMTP_SEND_MESSAGE_SUCCESS"  // (from_addr) +] + +const ACCOUNT_NOTICES = [ +  "IMAP_CLIENT_LOGIN",  // (username) + +  "MAIL_FETCHED_INCOMING",  // (userid) +  "MAIL_MSG_DECRYPTED",  // (userid) +  "MAIL_MSG_DELETED_INCOMING",  // (userid) +  "MAIL_MSG_PROCESSING",  // (userid) +  "MAIL_MSG_SAVED_LOCALLY",  // (userid) + +  "SMTP_RECIPIENT_ACCEPTED_ENCRYPTED",  // (userid, dest) +  "SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED",  // (userid, dest) +  "SMTP_RECIPIENT_REJECTED",  // (userid, dest) +  "SMTP_SEND_MESSAGE_ERROR"  // (userid, dest) +] + +const STATUSES = [ +  "KEYMANAGER_FINISHED_KEY_GENERATION",  // (address) +  "KEYMANAGER_STARTED_KEY_GENERATION",  // (address) +  "SMTP_SERVICE_STARTED", +  "MAIL_UNREAD_MESSAGES",  // (userid, number) +  "IMAP_SERVICE_STARTED" +] + +const STATUS_ERRORS = [ +  "IMAP_SERVICE_FAILED_TO_START", +  "IMAP_UNHANDLED_ERROR", +  "SMTP_SERVICE_FAILED_TO_START", +  "SMTP_CONNECTION_LOST",  // (userid, dest) +] +  export default class EmailSection extends React.Component {    static get defaultProps() {return{ @@ -14,17 +61,46 @@ export default class EmailSection extends React.Component {    constructor(props) {      super(props)      this.state = { -      status: null +      status: 'unknown', // on, off, unknown, wait, disabled, error +      messages: [], +      expanded: true      } +    this.expand    = this.expand.bind(this)      this.openKeys  = this.openKeys.bind(this)      this.openApp   = this.openApp.bind(this)      this.openPrefs = this.openPrefs.bind(this) +    this.logEvent  = this.logEvent.bind(this) +  } + +  componentWillMount() { +    let events = [].concat(GENERAL_NOTICES, ACCOUNT_NOTICES, STATUSES, STATUS_ERRORS) +    for (let event of events) { +      bitmask.events.register(event, this.logEvent) +    } +    bitmask.mail.status().then(status => { +      // either 'running' or 'disabled' +      let newstatus = 'error' +      if (status['mail'] == 'running') { +        newstatus = 'on' +      } else if (status['mail'] == 'disabled') { +        newstatus = 'disabled' +      } +      this.setState({status: newstatus}) +    }) +  } + +  logEvent(event, msg) { +    console.log("EVENT: " + event, msg)    }    openKeys() {}    openApp() {}    openPrefs() {} +  expand() { +    this.setState({expanded: !this.state.expanded}) +  } +    render () {      //let message = null      //if (this.state.error) { @@ -34,13 +110,22 @@ export default class EmailSection extends React.Component {      //  )      //}      let button = null -    if (this.state.status == 'ready') { +    let body = null +    let header = <h1>Mail</h1> +    if (this.state.status == 'on') {        button = <Button onClick={this.openApp}>Open Email</Button>      } +    if (this.state.status == 'disabled') { +      header = <h1>Mail Disabled</h1> +    } +    if (this.state.expanded) { +      body = ( +        <IMAPButton account={this.props.account} /> +      ) +    }      return ( -      <SectionLayout icon="envelope" status="on" button={button}> -        <h1>inbox: </h1> -      </SectionLayout> +      <SectionLayout icon="envelope" status={this.state.status} +        onExpand={this.expand} buttons={button} header={header} body={body} />      )    }  } diff --git a/ui/app/components/main_panel/imap_button.js b/ui/app/components/main_panel/imap_button.js new file mode 100644 index 0000000..98d8bad --- /dev/null +++ b/ui/app/components/main_panel/imap_button.js @@ -0,0 +1,121 @@ +// +// Button to show details for configuring mail clients +// + +import React from 'react' +import { Modal, Form, FormGroup, ControlLabel, FormControl, Col, Label, Button} from 'react-bootstrap' +import Account from 'models/account' +import bitmask from 'lib/bitmask' + +export default class IMAPButton extends React.Component { + +  static get defaultProps() {return{ +    account: null, +    title: "Connect Mail Client" +  }} + +  constructor(props) { +    super(props) +    this.state = { +      showModal: false, +      imapPort: '1984', +      smtpPort: '2013', +      token: '' +    } +    this.onClick = this.onClick.bind(this) +    this.onClose = this.onClose.bind(this) +  } + +  onClose() { +    this.setState({showModal: false}) +  } + +  onClick() { +    if (!this.state.token) { +      bitmask.mail.get_token().then(response => { +        if (response.user == this.props.account.address) { +          this.setState({token: response.token}) +        } +      }) +    } +    this.setState({showModal: true}) +  } + +  componentWillMount() {} + +  // don't allow fields to be changed +  onChange() {} + +  render () { +    let rowStyle = {height: '30px'} // to match bootstrap's input element height +    let form = null +    let modal = null + +    if (this.state.showModal) { +      form = ( +        <Form horizontal> +          <p> +             You can use any application that supports IMAP to read and send +             email through Bitmask. +          </p> +          <h3>Configuration for Thunderbird</h3> +          <p> +             For Thunderbird, you can use the Bitmask extension. Search for +             "Bitmask" in Thunderbird's add-on manager. +          </p> +          <h3>Configuration for other mail clients</h3> +          <p> +            Alternately, configure your mail client with the following options: +          </p> +          <FormGroup> +            <Col sm={2} componentClass={ControlLabel}>Username</Col> +            <Col sm={10}> +              <FormControl value={this.props.account.address} onChange={this.onChange}/> +            </Col> +          </FormGroup> +          <FormGroup> +            <Col sm={2} componentClass={ControlLabel}>Password</Col> +            <Col sm={10}> +              <FormControl value={this.state.token} onChange={this.onChange}/> +            </Col> +          </FormGroup> +          <FormGroup> +            <Col sm={2} componentClass={ControlLabel}>IMAP</Col> +            <Col sm={10} className="center-vertical" style={rowStyle}> +              <div className="center-item"> +                <Label>Host</Label> localhost +                    +                <Label>Port</Label> {this.state.imapPort} +              </div> +            </Col> +          </FormGroup> +          <FormGroup> +            <Col sm={2} componentClass={ControlLabel}>SMTP</Col> +            <Col sm={10} className="center-vertical" style={rowStyle}> +              <div className="center-item"> +                <Label>Host</Label> localhost +                    +                <Label>Port</Label> {this.state.smtpPort} +              </div> +            </Col> +          </FormGroup> +        </Form> +      ) +      modal = ( +        <Modal show={true} onHide={this.onClose}> +          <Modal.Header closeButton> +            <Modal.Title>{this.props.title}</Modal.Title> +          </Modal.Header> +           <Modal.Body> +            {form} +          </Modal.Body> +        </Modal> +      ) +    } + +    return ( +      <Button onClick={this.onClick}>{this.props.title} {modal}</Button> +    ) +  } + +} diff --git a/ui/app/components/main_panel/main_panel.less b/ui/app/components/main_panel/main_panel.less index 2e97913..7383a6c 100644 --- a/ui/app/components/main_panel/main_panel.less +++ b/ui/app/components/main_panel/main_panel.less @@ -229,6 +229,7 @@    }    .icon {      padding-right: @section-padding; +    height: @icon-size;      img {        width: @icon-size;        height: @icon-size; diff --git a/ui/app/css/common.css b/ui/app/css/common.css index fcae7fa..a0d2c35 100644 --- a/ui/app/css/common.css +++ b/ui/app/css/common.css @@ -63,15 +63,38 @@ body {   */  .center-container { -    display: -webkit-flex;    display: flex; +    -webkit-flex-flow: row nowrap;    flex-flow: row nowrap; +    -webkit-justify-content: center;    justify-content: center; + +  -webkit-align-content: center; +  align-content: center; + +  -webkit-align-items: center; +  align-items: center; +} + +.center-vertical { +  display: -webkit-flex; +  display: flex; + +  -webkit-flex-direction: row; +  flex-direction: row; + +  -webkit-flex-flow: row nowrap; +  flex-flow: row nowrap; + +  -webkit-justify-content: flex-start; +  justify-content: flex-start; +    -webkit-align-content: center;    align-content: center; +    -webkit-align-items: center;    align-items: center;  } @@ -84,7 +107,8 @@ body {    width: 100%;  } -.center-container .center-item { +.center-container .center-item, +.center-vertical .center-item {    -webkit-flex: 0 1 auto;    flex: 0 1 auto;  } diff --git a/ui/app/img/error.svg b/ui/app/img/error.svg new file mode 100644 index 0000000..69024a4 --- /dev/null +++ b/ui/app/img/error.svg @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg +   xmlns:dc="http://purl.org/dc/elements/1.1/" +   xmlns:cc="http://creativecommons.org/ns#" +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" +   xmlns:svg="http://www.w3.org/2000/svg" +   xmlns="http://www.w3.org/2000/svg" +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" +   width="32px" +   height="32px" +   id="svg3243" +   version="1.1" +   inkscape:version="0.91 r13725" +   sodipodi:docname="error.svg"> +  <defs +     id="defs3245" /> +  <sodipodi:namedview +     id="base" +     pagecolor="#ffffff" +     bordercolor="#666666" +     borderopacity="1.0" +     inkscape:pageopacity="0.0" +     inkscape:pageshadow="2" +     inkscape:zoom="11.313709" +     inkscape:cx="13.669808" +     inkscape:cy="12.217922" +     inkscape:current-layer="layer1" +     showgrid="true" +     inkscape:grid-bbox="true" +     inkscape:document-units="px" +     inkscape:snap-global="false" +     inkscape:window-width="1330" +     inkscape:window-height="813" +     inkscape:window-x="473" +     inkscape:window-y="143" +     inkscape:window-maximized="0"> +    <inkscape:grid +       type="xygrid" +       id="grid3273" /> +  </sodipodi:namedview> +  <metadata +     id="metadata3248"> +    <rdf:RDF> +      <cc:Work +         rdf:about=""> +        <dc:format>image/svg+xml</dc:format> +        <dc:type +           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> +        <dc:title /> +      </cc:Work> +    </rdf:RDF> +  </metadata> +  <g +     id="layer1" +     inkscape:label="Layer 1" +     inkscape:groupmode="layer"> +    <path +       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#808080;stroke:#000000;stroke-width:1.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1" +       d="m 26.607099,9.8590516 c 1.095245,1.8715414 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222445 -0.547656,4.274746 -1.642901,6.156901 -1.095291,1.87153 -2.584007,3.354927 -4.466146,4.450195 C 20.269405,27.702363 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.8430986,26.607098 7.9715637,25.51183 6.4881674,24.028433 5.3929018,22.156903 4.2976327,20.274748 3.75,18.222447 3.75,16.000002 3.75,13.777574 4.2976327,11.730593 5.3929018,9.8590516 6.4881674,7.9769071 7.9715637,6.4881926 9.8430986,5.392906 11.725251,4.2976563 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269405,0.5476563 6.140953,1.642906 1.882139,1.0952866 3.370855,2.5840011 4.466146,4.4661456" +       id="path3275" +       inkscape:connector-curvature="0" /> +    <g +       id="g3758" +       transform="matrix(0.865313,0,0,0.865313,2.225876,2.050981)" +       style="fill:#d50000;stroke:#000000;stroke-opacity:1;stroke-width:0.88407316;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:round"> +      <path +         sodipodi:type="star" +         style="opacity:1;fill:#d50000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.73702008;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" +         id="path4142" +         sodipodi:sides="3" +         sodipodi:cx="16.06254" +         sodipodi:cy="16.047972" +         sodipodi:r1="8.6266594" +         sodipodi:r2="4.3161001" +         sodipodi:arg1="1.1296168" +         sodipodi:arg2="2.2594356" +         inkscape:flatsided="false" +         inkscape:rounded="0" +         inkscape:randomized="0" +         d="M 19.746178,23.848617 13.319714,19.380482 7.4651642,15.337774 14.547914,12.006359 20.976278,8.9575249 20.319992,16.757074 Z" +         inkscape:transform-center-x="0.0023687104" +         inkscape:transform-center-y="-2.2364873" +         transform="matrix(0.98627727,-0.68272586,0.68272586,0.98627727,-10.759086,12.115158)" /> +    </g> +    <rect +       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.06500006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" +       id="rect4163" +       width="1.5" +       height="1.5" +       x="15.275888" +       y="18.618069" +       ry="0.9375" +       rx="0.9375" /> +    <rect +       rx="0.9375" +       ry="0.93750006" +       y="10.975301" +       x="15.272097" +       height="6.9514756" +       width="1.5" +       id="rect4165" +       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.06500006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> +  </g> +</svg> | 
