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 --- .gitignore | 6 +- service/pixelated/resources/login_resource.py | 13 + service/pixelated/resources/root_resource.py | 14 + web-ui/package.json | 2 +- web-ui/public/signup.css | 174 ++++++++++ web-ui/public/signup.html | 19 + web-ui/signup/css/normalize.css | 461 ------------------------- web-ui/signup/css/style.css | 174 ---------- web-ui/signup/images/pixelated-logo-orange.svg | 29 -- web-ui/signup/images/sent_email.png | Bin 9160 -> 0 bytes web-ui/signup/index.html | 18 - web-ui/signup/src/index.js | 223 ------------ web-ui/src/js/index.js | 223 ++++++++++++ 13 files changed, 446 insertions(+), 910 deletions(-) create mode 100644 web-ui/public/signup.css create mode 100644 web-ui/public/signup.html delete mode 100644 web-ui/signup/css/normalize.css delete mode 100644 web-ui/signup/css/style.css delete mode 100644 web-ui/signup/images/pixelated-logo-orange.svg delete mode 100644 web-ui/signup/images/sent_email.png delete mode 100644 web-ui/signup/index.html delete mode 100644 web-ui/signup/src/index.js create mode 100644 web-ui/src/js/index.js diff --git a/.gitignore b/.gitignore index b53ecd20..155686f0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,8 @@ *.egg-info web-ui/node_modules web-ui/app/bower_components -web-ui/target -web-ui/signup/bundle.css -web-ui/signup/bundle.js -web-ui/signup/lib/ +web-ui/lib/ +web-ui/public/signup.js .tmp .sass-cache/ dist/ diff --git a/service/pixelated/resources/login_resource.py b/service/pixelated/resources/login_resource.py index d5555b90..905b872c 100644 --- a/service/pixelated/resources/login_resource.py +++ b/service/pixelated/resources/login_resource.py @@ -39,6 +39,17 @@ def _get_startup_folder(): return os.path.join(path, '..', 'assets') +def _get_public_folder(): + static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "public")) + # this is a workaround for packaging + if not os.path.exists(static_folder): + static_folder = os.path.abspath( + os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "public")) + if not os.path.exists(static_folder): + static_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent') + return static_folder + + def _get_static_folder(): static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app")) # this is a workaround for packaging @@ -107,6 +118,7 @@ class LoginResource(BaseResource): def __init__(self, services_factory, provider=None, disclaimer_banner=None, authenticator=None): BaseResource.__init__(self, services_factory) self._static_folder = _get_static_folder() + self._public_folder = _get_public_folder() self._startup_folder = _get_startup_folder() self._disclaimer_banner = disclaimer_banner self._provider = provider @@ -114,6 +126,7 @@ class LoginResource(BaseResource): self._bootstrap_user_services = BootstrapUserServices(services_factory, provider) self.putChild('startup-assets', File(self._startup_folder)) + self.putChild('public-assets', File(self._public_folder)) with open(os.path.join(self._startup_folder, 'Interstitial.html')) as f: self.interstitial = f.read() diff --git a/service/pixelated/resources/root_resource.py b/service/pixelated/resources/root_resource.py index 8df76c70..8fa80bb2 100644 --- a/service/pixelated/resources/root_resource.py +++ b/service/pixelated/resources/root_resource.py @@ -51,6 +51,7 @@ class RootResource(BaseResource): def __init__(self, services_factory): BaseResource.__init__(self, services_factory) self._startup_assets_folder = self._get_startup_folder() + self._public_assets_folder = self._get_public_folder() self._static_folder = self._get_static_folder() self._html_template = open(os.path.join(self._static_folder, 'index.html')).read() self._services_factory = services_factory @@ -61,6 +62,7 @@ class RootResource(BaseResource): def _startup_mode(self): self.putChild('startup-assets', File(self._startup_assets_folder)) + self.putChild('public-assets', File(self._public_assets_folder)) self._mode = MODE_STARTUP def getChild(self, path, request): @@ -106,10 +108,22 @@ class RootResource(BaseResource): self._mode = MODE_RUNNING + # TODO: use the public folder for this def _get_startup_folder(self): path = os.path.dirname(os.path.abspath(__file__)) return os.path.join(path, '..', 'assets') + def _get_public_folder(self): + public_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "public")) + # this is a workaround for packaging + if not os.path.exists(public_folder): + public_folder = os.path.abspath( + os.path.join(os.path.abspath(__file__), "..", "..", "..", "..", "web-ui", "public")) + if not os.path.exists(public_folder): + # TODO: how is this packaged? + public_folder = os.path.join('/', 'usr', 'share', 'pixelated-user-agent') + return public_folder + def _get_static_folder(self): static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app")) # this is a workaround for packaging diff --git a/web-ui/package.json b/web-ui/package.json index c655c253..b937502f 100644 --- a/web-ui/package.json +++ b/web-ui/package.json @@ -36,7 +36,7 @@ "compass": "compass compile", "compass-watch": "compass watch", "build": "npm run clean && npm run handlebars && npm run add_git_version && npm run compass && npm run build-signup", - "build-signup": "babel signup/src -d signup/lib && browserify signup/lib/index.js >signup/bundle.js", + "build-signup": "babel src/js -d lib/js && browserify lib/js/index.js >public/signup.js", "jshint": "node_modules/jshint/bin/jshint --config=.jshintrc app test", "clean": "rm -rf .tmp/ 'dist/*' app/js/generated/hbs/* app/css/*", "buildmain": "node_modules/requirejs/bin/r.js -o config/buildoptions.js", diff --git a/web-ui/public/signup.css b/web-ui/public/signup.css new file mode 100644 index 00000000..61ac8587 --- /dev/null +++ b/web-ui/public/signup.css @@ -0,0 +1,174 @@ +body { + font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; +} + +.field-group { + position:relative; + margin-bottom: 35px; +} + +label { + font-size: 0.9em; + margin-bottom: 10px; + display: inline-block; +} + +input { + display: block; + border: solid 1px #4da3b6; + width: 100%; + height: auto; + padding: 10px 5px; + margin-bottom: 20px; +} + +.animated-label { + color:#999; + position:absolute; + pointer-events:none; + left: 6px; + top:10px; + transition:0.2s ease all; + -moz-transition:0.2s ease all; + -webkit-transition:0.2s ease all; +} + +input:focus { + outline:none; +} + +input:focus ~ .animated-label, input:valid ~ .animated-label{ + top:-20px; + left: 0; + font-size:0.8em; + color:#4da3b6; +} + +.blue-button { + background: #178ca6; + color: white; + display: block; + text-decoration: none; + text-align: center; + padding: 10px 0 10px 0; + width: 104%; + margin: 0 auto; +} + +.blue-button:hover { + background: #4da3b6; +} + +a { + text-decoration: none; + color: #4da3b6; +} + +h1 { + font-size: 1.5em; + text-align: center; +} + +header { + width: 18%; + margin: 0 auto; +} + +.link-message { + text-align: center; + font-size: 0.8em; +} + +.logo { + width: 100%; + height: auto; + padding-top: 20%; + margin-bottom: 30px; +} + +.message h1 { + margin-bottom: 35px; +} + +.message p { + padding-left: 5%; + padding-right: 5%; + width: 40%; + margin: 0 auto; + text-align: center; + line-height: 1.8em; + font-size: 0.9em; +} + +.form-container { + width: 20%; + margin: 0 auto; + padding-top: 40px; +} + +.domain-label { + position: relative; + top: 26px; + padding-left: 20px; + left: 100%; +} + +.sent-email-icon { + width: 60px; +} + +.disabled { + pointer-events: none; + background: #d4d4d4; +} + +.link-message .disabled { + pointer-events: none; + color: #d4d4d4; + background: none; +} + +/* Medium Devices, Desktops */ +@media only screen and (max-width : 992px) { + header { + width: 20%; + } + + .form-container { + width: 30%; + } + + .message p { + width: 70% + } +} + +/* Small Devices, Tablets */ +@media only screen and (max-width : 768px) { + header { + width: 30%; + } + + .form-container { + width: 50%; + } + + .message p { + width: 80% + } +} + +/* Extra Small Devices, Phones */ +@media only screen and (max-width : 480px) { + header { + width: 60%; + } + + .form-container { + width: 80%; + } + + .message p { + width: 85% + } +} diff --git a/web-ui/public/signup.html b/web-ui/public/signup.html new file mode 100644 index 00000000..9bc6cdad --- /dev/null +++ b/web-ui/public/signup.html @@ -0,0 +1,19 @@ + + + + + + Pixelated Mail + + + + + + +
+
+
+ +
+ + diff --git a/web-ui/signup/css/normalize.css b/web-ui/signup/css/normalize.css deleted file mode 100644 index 9b77e0eb..00000000 --- a/web-ui/signup/css/normalize.css +++ /dev/null @@ -1,461 +0,0 @@ -/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ - -/** - * 1. Change the default font family in all browsers (opinionated). - * 2. Correct the line height in all browsers. - * 3. Prevent adjustments of font size after orientation changes in - * IE on Windows Phone and in iOS. - */ - -/* Document - ========================================================================== */ - -html { - font-family: sans-serif; /* 1 */ - line-height: 1.15; /* 2 */ - -ms-text-size-adjust: 100%; /* 3 */ - -webkit-text-size-adjust: 100%; /* 3 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers (opinionated). - */ - -body { - margin: 0; -} - -/** - * Add the correct display in IE 9-. - */ - -article, -aside, -footer, -header, -nav, -section { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - * 1. Add the correct display in IE. - */ - -figcaption, -figure, -main { /* 1 */ - display: block; -} - -/** - * Add the correct margin in IE 8. - */ - -figure { - margin: 1em 40px; -} - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * 1. Remove the gray background on active links in IE 10. - * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. - */ - -a { - background-color: transparent; /* 1 */ - -webkit-text-decoration-skip: objects; /* 2 */ -} - -/** - * Remove the outline on focused links when they are also active or hovered - * in all browsers (opinionated). - */ - -a:active, -a:hover { - outline-width: 0; -} - -/** - * 1. Remove the bottom border in Firefox 39-. - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Prevent the duplicate application of `bolder` by the next rule in Safari 6. - */ - -b, -strong { - font-weight: inherit; -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font style in Android 4.3-. - */ - -dfn { - font-style: italic; -} - -/** - * Add the correct background and color in IE 9-. - */ - -mark { - background-color: #ff0; - color: #000; -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -audio, -video { - display: inline-block; -} - -/** - * Add the correct display in iOS 4-7. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Remove the border on images inside links in IE 10-. - */ - -img { - border-style: none; -} - -/** - * Hide the overflow in IE. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers (opinionated). - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: sans-serif; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` - * controls in Android 4. - * 2. Correct the inability to style clickable types in iOS and Safari. - */ - -button, -html [type="button"], /* 1 */ -[type="reset"], -[type="submit"] { - -webkit-appearance: button; /* 2 */ -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Change the border, margin, and padding in all browsers (opinionated). - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * 1. Add the correct display in IE 9-. - * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - display: inline-block; /* 1 */ - vertical-align: baseline; /* 2 */ -} - -/** - * Remove the default vertical scrollbar in IE. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10-. - * 2. Remove the padding in IE 10-. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in IE 9-. - * 1. Add the correct display in Edge, IE, and Firefox. - */ - -details, /* 1 */ -menu { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Scripting - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -canvas { - display: inline-block; -} - -/** - * Add the correct display in IE. - */ - -template { - display: none; -} - -/* Hidden - ========================================================================== */ - -/** - * Add the correct display in IE 10-. - */ - -[hidden] { - display: none; -} diff --git a/web-ui/signup/css/style.css b/web-ui/signup/css/style.css deleted file mode 100644 index 61ac8587..00000000 --- a/web-ui/signup/css/style.css +++ /dev/null @@ -1,174 +0,0 @@ -body { - font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; -} - -.field-group { - position:relative; - margin-bottom: 35px; -} - -label { - font-size: 0.9em; - margin-bottom: 10px; - display: inline-block; -} - -input { - display: block; - border: solid 1px #4da3b6; - width: 100%; - height: auto; - padding: 10px 5px; - margin-bottom: 20px; -} - -.animated-label { - color:#999; - position:absolute; - pointer-events:none; - left: 6px; - top:10px; - transition:0.2s ease all; - -moz-transition:0.2s ease all; - -webkit-transition:0.2s ease all; -} - -input:focus { - outline:none; -} - -input:focus ~ .animated-label, input:valid ~ .animated-label{ - top:-20px; - left: 0; - font-size:0.8em; - color:#4da3b6; -} - -.blue-button { - background: #178ca6; - color: white; - display: block; - text-decoration: none; - text-align: center; - padding: 10px 0 10px 0; - width: 104%; - margin: 0 auto; -} - -.blue-button:hover { - background: #4da3b6; -} - -a { - text-decoration: none; - color: #4da3b6; -} - -h1 { - font-size: 1.5em; - text-align: center; -} - -header { - width: 18%; - margin: 0 auto; -} - -.link-message { - text-align: center; - font-size: 0.8em; -} - -.logo { - width: 100%; - height: auto; - padding-top: 20%; - margin-bottom: 30px; -} - -.message h1 { - margin-bottom: 35px; -} - -.message p { - padding-left: 5%; - padding-right: 5%; - width: 40%; - margin: 0 auto; - text-align: center; - line-height: 1.8em; - font-size: 0.9em; -} - -.form-container { - width: 20%; - margin: 0 auto; - padding-top: 40px; -} - -.domain-label { - position: relative; - top: 26px; - padding-left: 20px; - left: 100%; -} - -.sent-email-icon { - width: 60px; -} - -.disabled { - pointer-events: none; - background: #d4d4d4; -} - -.link-message .disabled { - pointer-events: none; - color: #d4d4d4; - background: none; -} - -/* Medium Devices, Desktops */ -@media only screen and (max-width : 992px) { - header { - width: 20%; - } - - .form-container { - width: 30%; - } - - .message p { - width: 70% - } -} - -/* Small Devices, Tablets */ -@media only screen and (max-width : 768px) { - header { - width: 30%; - } - - .form-container { - width: 50%; - } - - .message p { - width: 80% - } -} - -/* Extra Small Devices, Phones */ -@media only screen and (max-width : 480px) { - header { - width: 60%; - } - - .form-container { - width: 80%; - } - - .message p { - width: 85% - } -} diff --git a/web-ui/signup/images/pixelated-logo-orange.svg b/web-ui/signup/images/pixelated-logo-orange.svg deleted file mode 100644 index 7e0ef43d..00000000 --- a/web-ui/signup/images/pixelated-logo-orange.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/web-ui/signup/images/sent_email.png b/web-ui/signup/images/sent_email.png deleted file mode 100644 index ddaa11d0..00000000 Binary files a/web-ui/signup/images/sent_email.png and /dev/null differ diff --git a/web-ui/signup/index.html b/web-ui/signup/index.html deleted file mode 100644 index a1836721..00000000 --- a/web-ui/signup/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Pixelated Mail - - - - - -
-
-
- -
- - diff --git a/web-ui/signup/src/index.js b/web-ui/signup/src/index.js deleted file mode 100644 index 35de6407..00000000 --- a/web-ui/signup/src/index.js +++ /dev/null @@ -1,223 +0,0 @@ -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') -); 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