diff options
-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> |