From 6cdfb2110b0f96502eeaaf98f59a05704534cdff Mon Sep 17 00:00:00 2001 From: Roald de Vries Date: Fri, 18 Nov 2016 13:47:32 +0100 Subject: serve signup page through twisted --- web-ui/src/js/index.js | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 web-ui/src/js/index.js (limited to 'web-ui/src') diff --git a/web-ui/src/js/index.js b/web-ui/src/js/index.js new file mode 100644 index 00000000..35de6407 --- /dev/null +++ b/web-ui/src/js/index.js @@ -0,0 +1,223 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {createStore} from 'redux'; +import {Map} from 'immutable'; +import 'whatwg-fetch'; + + +class PixelatedComponent extends React.Component { + _updateStateFromStore() { + this.setState(this.props.store.getState().toJS()); + } + + componentWillMount() { + this.unsubscribe = this.props.store.subscribe(() => this._updateStateFromStore()); + this._updateStateFromStore(); + } + + componentWillUnmount() { + this.unsubscribe() + } +} + + +class PixelatedForm extends PixelatedComponent { + _fetchAndDispatch(url, actionProperties) { + const immutableActionProperties = new Map(actionProperties); + this.props.store.dispatch(immutableActionProperties.merge({status: 'STARTED'}).toJS()); + fetch(url).then((response) => { + console.debug('got a reply', response); + return response.json() + }).then((json) => { + console.debug('got json', json); + setTimeout(() => { + this.props.store.dispatch(immutableActionProperties.merge({status: 'SUCCESS', json: json}).toJS()); + }, 3000); + }).catch((error) => { + console.error('something went wrong', error); + this.props.store.dispatch(immutableActionProperties.merge({status: 'ERROR', error: error}).toJS()); + }); + } +} + + +class InviteCodeForm extends PixelatedForm { + render() { + return ( +
+
+ + +
+ +
+ ); + } + + _handleClick(event) { + event.stopPropagation(); + event.preventDefault(); + this.props.store.dispatch({type: 'SUBMIT_INVITE_CODE', inviteCode: event.target['invite-code'].value}); + } +} + + +class CreateAccountForm extends PixelatedForm { + render() { + return ( +
+ @domain.com +
+ + +
+ +
+ + +
+ + +
+ ); + } + + _handleClick(event) { + event.stopPropagation(); + event.preventDefault(); + this.props.store.dispatch({type: 'SUBMIT_CREATE_ACCOUNT', username: event.target['username'].value, password: event.target['password'].value}); + } +} + + +class BackupEmailForm extends PixelatedForm { + render() { + return ( +
+
+ + +
+ + +

+ I didn't receive anything. Send the email again +

+
+ ); + } + + _handleClick(event) { + event.stopPropagation(); + event.preventDefault(); + this._fetchAndDispatch('dummy.json', {type: 'SUBMIT_BACKUP_EMAIL', backupEmail: event.target['backup-email'].value}); + } +} + + +class BackupEmailSentForm extends PixelatedForm { + render() { + return ( +
+ {this.state.isFetching || I received the codes.
Go to my inbox
} +

+ I didn't receive anything. Send the email again +

+
+ ); + } + + _handleClick(event) { + event.stopPropagation(); + event.preventDefault(); + } +} + + +class SignUp extends PixelatedComponent { + render() { + return ( +
+
+

{this.state.header}

+ {this.state.icon} +

{this.state.summary}

+
+
+ {this._form()} +
+
+ ); + } + + _form() { + switch(this.state.form) { + case 'invite_code': return ; + case 'create_account': return ; + case 'backup_email': return ; + case 'backup_email_sent': return ; + default: throw Error('TODO'); + } + } +} + + +const initialState = new Map({ + isFetching: false, + form: 'invite_code', + header: 'Welcome', + icon: null, + summary: ['Do you have an invite code?',
, 'Type it below'], +}); + + +const store = createStore((state=initialState, action) => { + switch (action.type) { + case 'SUBMIT_INVITE_CODE': + return state.merge({ + inviteCode: action.inviteCode, + form: 'create_account', + header: 'Create your account', + summary: 'Choose your username, and be careful about your password, it must be strong and easy to remember. If you have a password manager, we strongly advise you to use one.', + }); + case 'SUBMIT_CREATE_ACCOUNT': + return state.merge({ + username: action.username, + password: action.password, + form: 'backup_email', + header: 'In case you lose your password...', + summary: 'Set up a backup email account. You\'ll receive an email with a code so you can recover your account in the future, other will be sent to your account administrator.', + }); + case 'SUBMIT_BACKUP_EMAIL': + switch (action.status) { + case 'STARTED': + return state.merge({ + isFetching: true, + backupEmail: action.backupEmail, + form: 'backup_email_sent', + icon:

, + summary: 'An email was sent to the email you provided. Check your spam folder, just in case.', + }); + case 'SUCCESS': + return state.merge({ + isFetching: false, + }); + case 'ERROR': + return state.merge({ + isFetching: false, + }); + default: + return state; + } + case 'SUBMIT_BACKUP_EMAIL_SENT': + return state.merge({}); + default: + return state; + } +}); + + +ReactDOM.render( + , + document.getElementById('app') +); -- cgit v1.2.3