1
0
Fork 0
mirror of https://github.com/anyproto/anytype-ts.git synced 2025-06-08 05:57:02 +09:00

auth flow

This commit is contained in:
Razor 2019-09-13 12:54:36 +03:00
parent c3100bda49
commit 65923da275
29 changed files with 613 additions and 198 deletions

View file

@ -28,10 +28,13 @@ function createWindow () {
win.loadURL('http://localhost:8080');
ipcMain.on('appLoaded', () => {
pipe = Pipe.start();
pipe.read((event) => {
console.log('appLoaded');
pipe = new Pipe((event) => {
console.log('EVENT', event);
win.webContents.send('pipeEvent', event);
});
pipe.start();
});
ipcMain.on('pipeCmd', (e, data) => {

View file

@ -50,6 +50,7 @@
"child_process": "^1.0.2",
"css-loader": "^3.2.0",
"electron": "^6.0.8",
"emoji-mart": "^2.11.1",
"fifo-js": "^2.1.0",
"fs": "0.0.1-security",
"history": "^4.10.0",

View file

@ -11,9 +11,12 @@ const GO_TMP = '/var/tmp/.go_pipe';
class Pipe {
constructor () {
constructor (onMessage) {
this.cp = null;
this.fifo = null;
this.onMessage = onMessage;
return this;
};
start () {
@ -27,11 +30,22 @@ class Pipe {
return;
};
console.log('Fifo created: ' + JS_TMP);
this.fifo = new FIFO(JS_TMP);
console.log('Fifo created:', this.fifo);
let rl = readline.createInterface({ input: fs.createReadStream(GO_TMP) });
console.log('ReadLine', rl);
rl.on('line', (line) => {
let msg = atob(line.slice(0, -1));
msg = Event.decode(Buffer.from(msg));
console.log('Pipe.read', msg);
this.onMessage(msg);
});
});
return this;
};
stop () {
@ -51,18 +65,6 @@ class Pipe {
console.log('Pipe.write', msg);
};
read (cb) {
let rl = readline.createInterface({ input: fs.createReadStream(GO_TMP) });
rl.on('line', (line) => {
// b64 -> msg + remove \n at the end
const msg = atob(line.slice(0, -1));
cb(Event.decode(Buffer.from(msg)));
console.log('Pipe.read', msg);
});
};
generateId () {
let chars = '0123456789ABCDEF'.split('');
let len = 32;
@ -72,4 +74,4 @@ class Pipe {
};
module.exports = new Pipe();
module.exports = Pipe;

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<circle cx="11" cy="11" r="10" fill-rule="nonzero" stroke="#d2d0c2" stroke-width="2"/>
<path fill="#d2d0c2" d="M7.394 8.495c.068-1.994 1.439-3.44 3.937-3.44 2.305 0 3.844 1.329 3.844 3.188 0 1.287-.64 2.187-1.783 2.868-1.102.648-1.413 1.077-1.413 1.91v.948h-1.952l-.008-1.041c-.076-1.262.412-2.019 1.573-2.7 1.043-.631 1.396-1.077 1.396-1.91 0-.874-.706-1.505-1.766-1.505-1.085 0-1.784.656-1.85 1.682H7.393z"/>
<path fill="#d2d0c2" fill-rule="nonzero" d="M10 15h2v2h-2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 637 B

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<circle cx="11" cy="11" r="10" fill-rule="nonzero" stroke="#000" stroke-width="2"/>
<path fill="#000" d="M7.394 8.495c.068-1.994 1.439-3.44 3.937-3.44 2.305 0 3.844 1.329 3.844 3.188 0 1.287-.64 2.187-1.783 2.868-1.102.648-1.413 1.077-1.413 1.91v.948h-1.952l-.008-1.041c-.076-1.262.412-2.019 1.573-2.7 1.043-.631 1.396-1.077 1.396-1.91 0-.874-.706-1.505-1.766-1.505-1.085 0-1.784.656-1.85 1.682H7.393z"/>
<path fill="#000" fill-rule="nonzero" d="M10 15h2v2h-2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 628 B

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<circle cx="11" cy="11" r="10" fill-rule="nonzero" stroke="#fff" stroke-width="2"/>
<path fill="#fff" d="M7.394 8.495c.068-1.994 1.439-3.44 3.937-3.44 2.305 0 3.844 1.329 3.844 3.188 0 1.287-.64 2.187-1.783 2.868-1.102.648-1.413 1.077-1.413 1.91v.948h-1.952l-.008-1.041c-.076-1.262.412-2.019 1.573-2.7 1.043-.631 1.396-1.077 1.396-1.91 0-.874-.706-1.505-1.766-1.505-1.085 0-1.784.656-1.85 1.682H7.393z"/>
<path fill="#fff" fill-rule="nonzero" d="M10 15h2v2h-2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 628 B

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<circle cx="11" cy="11" r="10" fill-rule="nonzero" stroke="#000" stroke-width="2"/>
<path fill="#000" d="M7.394 8.495c.068-1.994 1.439-3.44 3.937-3.44 2.305 0 3.844 1.329 3.844 3.188 0 1.287-.64 2.187-1.783 2.868-1.102.648-1.413 1.077-1.413 1.91v.948h-1.952l-.008-1.041c-.076-1.262.412-2.019 1.573-2.7 1.043-.631 1.396-1.077 1.396-1.91 0-.874-.706-1.505-1.766-1.505-1.085 0-1.784.656-1.85 1.682H7.393z"/>
<path fill="#000" fill-rule="nonzero" d="M10 15h2v2h-2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 627 B

View file

@ -2,10 +2,13 @@
{ "path": "/" },
{ "path": "/auth/code" },
{ "path": "/auth/notice" },
{ "path": "/auth/select" },
{ "path": "/auth/register" },
{ "path": "/auth/login" },
{ "path": "/auth/pin-select" },
{ "path": "/auth/pin-confirm" },
{ "path": "/auth/setup" },
{ "path": "/auth/account-select" },
{ "path": "/auth/register" },
{ "path": "/main/index" },
{ "path": "/main/edit/:id?" }

View file

@ -2,7 +2,7 @@
html, body { height: 100%; }
body {
font-family: 'Lcg'; font-size: 15px; line-height: 1.6; letter-spacing: -0.1px; color: #000;
font-family: 'Lcg'; font-size: 15px; line-height: 24px; letter-spacing: -0.08px; color: #000;
overflow-x: hidden; overflow-y: scroll; background: #fff;
-webkit-font-smoothing: antialiased;
overscroll-behavior: none;
@ -28,4 +28,8 @@ input, textarea, select { font-family: 'Lcg'; }
.fileWrap { position: relative; overflow: hidden; cursor: pointer; }
.fileWrap {
.input-file { position: absolute; left: 0px; top: 0px; opacity: 0; width: 100%; height: 100%; cursor: pointer; z-index: 100; padding: 0px; }
}
}
#commonHelp { position: fixed; right: 12px; bottom: 12px; cursor: pointer; z-index: 11; }
nav { position: absolute; left: 50px; top: 50px; z-index: 100; }

View file

@ -5,9 +5,15 @@
background-position: center center; transition: $transitionFast; position: relative;
}
.icon.help { width: 22px; height: 22px; background-image: url('~img/icon/help/dark0.svg'); }
.icon.help:hover { background-image: url('~img/icon/help/dark1.svg'); }
.icon.help.light { background-image: url('~img/icon/help/light0.svg'); }
.icon.help.light:hover { background-image: url('~img/icon/help/light1.svg'); }
.icon.user {
width: 64px; height: 64px; border-radius: 100%; border: solid 2px #fff; font-weight: 400; position: relative;
text-transform: uppercase; font-size: 22px; color: #fff; background-color: #4fb1ef; text-align: center; line-height: 62px;
width: 64px; height: 64px; border-radius: 100%; font-weight: 600; position: relative; overflow: hidden;
text-transform: uppercase; font-size: 22px; color: #fff; background-color: #2dbcfe; text-align: center; line-height: 62px;
}
.icon.user {
.txt { line-height: inherit; position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 1; }

View file

@ -0,0 +1,10 @@
.smile {
display: inline-block; vertical-align: top; width: 24px; height: 24px; text-align: center; margin-right: 4px;
line-height: 24px;
}
.smile {
.emoji-mart-emoji { height: 100%; display: inline-block; vertical-align: middle; }
.emoji-mart-emoji {
span { height: 100%; display: inline-block; vertical-align: middle; }
}
}

View file

@ -1,66 +1,74 @@
.page-auth, .page-index-index {
.frame {
background: #fff; width: 672px; border-radius: 8px; padding: 40px 48px 32px 48px; position: fixed; left: 50%; top: 50%;
margin-left: -336px; z-index: 1;
.logo {
width: 60px; height: 13px; position: fixed; left: 50%; margin-left: -30px; top: 13px; background: url('~img/logo.svg') no-repeat;
background-position: center center;
}
.frame {
.title { font-size: 36px; font-weight: 600; letter-spacing: -0.7px; color: #393834; line-height: 1.11; margin-bottom: 18px; }
.label.bold { font-size: 18px; font-weight: 600; line-height: 1.44; letter-spacing: -0.1px; margin: 2px 0px 17px 0px; }
.input { height: 40px; border: solid 1px #d2d0c2; }
.copy { width: 120px; position: fixed; left: 50%; bottom: 12px; z-index: 1; text-align: center; margin-left: -60px; color: #fff; }
.frame {
width: 672px; position: fixed; left: 50%; top: 50%; margin-left: -336px; z-index: 1; background: #fff; border-radius: 8px;
padding: 36px 48px 32px 48px;
}
.title { font-size: 36px; line-height: 40px; font-weight: 600; letter-spacing: -0.72px; color: #000; margin-bottom: 15px; }
.label { margin-bottom: 25px; }
.input { height: 40px; border: solid 1px #d2d0c2; }
.textArea {
font-family: 'Mono'; padding: 7px 16px; height: auto; margin: 0px 0px 32px 0px; border: solid 1px #d2d0c2;
background-color: #fff;
}
.button { margin-right: 16px; }
.button:last-child { margin: 0px; }
}
.page-auth-code, .page-index-index {
.page-auth-select, .page-index-index {
.label { margin-bottom: 9px; }
}
.page-auth-login {
.label { margin-bottom: 9px; }
}
.page-auth-pin-select, .page-auth-pin-confirm {
.input { width: 40px; margin-right: 8px; padding: 0px; text-align: center; vertical-align: top; font-size: 18px; }
.input-password { font-size: 36px; }
.input:last-child { margin: 0px; }
}
.page-auth-setup {
.title { margin: 0px; }
.smile { width: 64px; height: 64px; border-radius: 100%; background-color: #f7f5f0; line-height: 64px; margin-bottom: 12px; }
.smile {
.emoji-mart-emoji { height: 36px; line-height: 36px; }
.emoji-mart-emoji { span { height: 36px; line-height: 36px; margin: -1px 0px 0px 2px; } }
}
}
.page-auth-account-select {
.frame { padding-bottom: 48px; }
.frame {
.title { font-size: 32px; font-weight: 600; letter-spacing: -0.5px; color: #393834; line-height: 1; margin-bottom: 18px; }
.label { margin-bottom: 4px; }
.input { width: 468px; margin-right: 16px; }
.button { width: 92px; vertical-align: top; }
}
}
.page-auth-notice {
.frame {
.button { margin-top: 24px; }
}
}
.page-auth-select {
.frame {
.title { font-size: 36px; }
.buttons { margin-top: 24px; }
.button { margin-right: 16px; }
.button:last-child { margin: 0px; }
.title { margin-bottom: 21px; }
.list { border-radius: 8px; border: 1px solid #e0ded2; }
.item { border-bottom: 1px solid #e0ded2; padding: 12px; cursor: pointer; }
.item:last-child { border: 0px; }
.item {
.icon.user { width: 48px; height: 48px; line-height: 50px; font-size: 21px; letter-spacing: -0.16px; margin-right: 12px; }
.name {
font-size: 18px; line-height: 26px; display: inline-block; vertical-align: middle; width: calc(100% - 60px); height: 26px;
overflow: hidden; font-weight: 600;
}
}
}
.page-auth-register {
.frame {
.inputWithImage { background: #f7f5f0; padding: 48px 48px 30px 48px; margin: 24px 0px 32px 0px; }
.inputWithImage {
.input-text {
font-size: 22px; font-weight: 600; line-height: 1.82; letter-spacing: -0.2px; margin-top: 11px;
height: 28px; padding: 0px; border: 0px;
}
.fileWrap { width: 64px; height: 64px; }
.inputWithImage { background: #f7f5f0; padding: 48px 48px 30px 48px; margin: 24px 0px 32px 0px; }
.inputWithImage {
.input-text {
font-size: 22px; font-weight: 600; line-height: 1.82; letter-spacing: -0.2px; margin-top: 11px;
height: 28px; padding: 0px; border: 0px;
}
.button { margin-right: 16px; }
.button:last-child { margin: 0px; }
}
}
.page-auth-login {
.frame {
.label { margin-bottom: 4px; }
.textArea {
font-family: 'Mono'; padding: 7px 16px; height: auto; margin: 0px 0px 32px 0px; border: solid 1px #d2d0c2;
background-color: #fff;
}
.button { margin-right: 16px; }
.button:last-child { margin: 0px; }
.fileWrap { width: 64px; height: 64px; }
}
}

View file

@ -18,6 +18,7 @@ import 'scss/component/input.scss';
import 'scss/component/button.scss';
import 'scss/component/icon.scss';
import 'scss/component/textArea.scss';
import 'scss/component/smile.scss';
import 'scss/page/auth.scss';
import 'scss/page/main/index.scss';
@ -55,11 +56,11 @@ class App extends React.Component<{}, {}> {
};
componentDidMount () {
ipcRenderer.send('appLoaded', true);
ipcRenderer.on('pipeEvent', (e: any, data: any) => {
Dispatcher.event(data);
});
ipcRenderer.send('appLoaded', true);
};
};

View file

@ -0,0 +1,17 @@
import * as React from 'react';
import { Icon } from 'ts/component';
class FooterAuth extends React.Component<{}, {}> {
render () {
return (
<div>
<div className="copy">2018, Anytype</div>
<Icon id="commonHelp" className="help light" onMouseDown={() => { }} />
</div>
);
};
};
export default FooterAuth;

View file

@ -0,0 +1,13 @@
import * as React from 'react';
class HeaderAuth extends React.Component<{}, {}> {
render () {
return (
<div className="logo" />
);
};
};
export default HeaderAuth;

View file

@ -1,13 +1,21 @@
import Page from './page';
import PageAuthCode from './page/auth/code';
import PageAuthNotice from './page/auth/notice';
import PageAuthSelect from './page/auth/select';
import PageAuthRegister from './page/auth/register';
import PageAuthLogin from './page/auth/login';
import PageAuthPinSelect from './page/auth/pin/select';
import PageAuthPinConfirm from './page/auth/pin/confirm';
import PageAuthSetup from './page/auth/setup';
import PageAuthAccountSelect from './page/auth/account/select';
import PageAuthRegister from './page/auth/register';
import PageMainIndex from './page/main/index';
import HeaderAuth from './header/auth';
import FooterAuth from './footer/auth';
import Title from './util/title';
import Label from './util/label';
import Smile from './util/smile';
import Input from './util/input';
import TextArea from './util/textArea';
@ -20,16 +28,22 @@ import IconUser from './util/iconUser';
export {
Page,
PageAuthCode,
PageAuthNotice,
PageAuthSelect,
PageAuthRegister,
PageAuthLogin,
PageAuthSelect,
PageAuthPinSelect,
PageAuthPinConfirm,
PageAuthSetup,
PageAuthAccountSelect,
PageAuthRegister,
PageMainIndex,
HeaderAuth,
FooterAuth,
Input,
TextArea,
Button,
Title,
Label,
Smile,
Error,
Icon,
IconUser,

View file

@ -1,7 +1,9 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { PageAuthCode, PageAuthNotice, PageAuthSelect, PageAuthRegister, PageAuthLogin, PageMainIndex } from '.';
import {
PageAuthCode, PageAuthSelect, PageAuthRegister, PageAuthLogin, PageAuthPinSelect, PageAuthPinConfirm,
PageAuthSetup, PageAuthAccountSelect, PageMainIndex } from '.';
const $ = require('jquery');
@ -14,15 +16,18 @@ class Page extends React.Component<Props, {}> {
render () {
const Components: any = {
'index/index': PageAuthCode,
'index/index': PageAuthSelect,
'auth/code': PageAuthCode,
'auth/notice': PageAuthNotice,
'auth/select': PageAuthSelect,
'auth/register': PageAuthRegister,
'auth/login': PageAuthLogin,
'auth/code': PageAuthCode,
'auth/select': PageAuthSelect,
'auth/register': PageAuthRegister,
'auth/login': PageAuthLogin,
'auth/pin-select': PageAuthPinSelect,
'auth/pin-confirm': PageAuthPinConfirm,
'auth/setup': PageAuthSetup,
'auth/account-select': PageAuthAccountSelect,
'main/index': PageMainIndex,
'main/index': PageMainIndex,
};
const route = this.getRoute();
@ -56,11 +61,14 @@ class Page extends React.Component<Props, {}> {
getRoute () {
const { match } = this.props;
let a: string[] = match.path.split('/');
a.shift();
return { page: a[0] || 'index', action: a[1] || 'index' };
return {
page: a[0] || 'index',
action: a[1] || 'index',
id: a[2] || '',
};
};
getClass () {

View file

@ -0,0 +1,76 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Title, IconUser, HeaderAuth as Header, FooterAuth as Footer } from 'ts/component';
const $ = require('jquery');
interface Profile {
icon: string;
name: string;
};
interface Props extends RouteComponentProps<any> {};
interface State {
list: Profile[];
};
class PageAccountSelect extends React.Component<Props, State> {
state = {
list: [
{ icon: '', color: '', name: 'Anton Pronin' },
{ icon: '', color: '', name: 'James Simon' },
{ icon: '', color: '', name: 'Tony Leung' }
]
};
constructor (props: any) {
super(props);
};
render () {
const { list } = this.state;
const Item = (item: Profile) => (
<div className="item">
<IconUser {...item} />
<div className="name">{item.name}</div>
</div>
);
return (
<div>
<div className="cover c3" />
<Header />
<Footer />
<div className="frame">
<Title text="Choose profile" />
<div className="list">
{list.map((item, i) => (
<Item key={i} {...item} />
))}
</div>
</div>
</div>
);
};
onSelect (e: any) {
e.preventDefault();
this.props.history.push('/main/index');
};
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('.frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
};
};
export default PageAccountSelect;

View file

@ -58,6 +58,7 @@ class PageAuthCode extends React.Component<Props, State> {
const code: string = this.codeRef.getValue();
Dispatcher.cmd({ entity: 'auth', op: 'code', data: code });
//this.props.history.push('/auth/notice');
};
resize () {

View file

@ -1,13 +1,11 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Title, Label, Error, TextArea, Button } from 'ts/component';
import { Title, Label, Error, TextArea, Button, HeaderAuth as Header, FooterAuth as Footer } from 'ts/component';
const $ = require('jquery');
interface Props extends RouteComponentProps<any> {
};
interface Props extends RouteComponentProps<any> {};
interface State {
error: string;
};
@ -32,15 +30,18 @@ class PageAuthLogin extends React.Component<Props, State> {
return (
<div>
<div className="cover c1" />
<div id="frame" className="frame">
<Title text="Sign in with your keychain" />
<div className="cover c3" />
<Header />
<Footer />
<div className="frame">
<Title text="Login with your keychain" />
<Label text="Type your keychain phrase or private key" />
<Error text={error} />
<TextArea ref={(ref: any) => this.phraseRef = ref} placeHolder="12 words separated by space" />
<TextArea ref={(ref: any) => this.phraseRef = ref} placeHolder="witch collapse practice feed shame open despair creek road again ice least lake tree young address brain envelope" />
<div className="buttons">
<Button text="Sign in" className="orange" onClick={this.onSubmit} />
<Button text="Login" className="orange" onClick={this.onSubmit} />
<Button text="Back" className="grey" onClick={this.onCancel} />
</div>
</div>
@ -50,7 +51,7 @@ class PageAuthLogin extends React.Component<Props, State> {
onSubmit (e: any) {
e.preventDefault();
this.props.history.push('/main/index');
this.props.history.push('/auth/pin-select');
};
onCancel (e: any) {
@ -60,7 +61,7 @@ class PageAuthLogin extends React.Component<Props, State> {
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('#frame');
let frame = node.find('.frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
};

View file

@ -1,43 +0,0 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Label, Button } from 'ts/component';
const $ = require('jquery');
interface Props extends RouteComponentProps<any> {
};
class PageAuthNotice extends React.Component<Props, {}> {
constructor (props: any) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
};
render () {
return (
<div className="frame">
<Label className="bold" text="Important notice" />
<Label text="Understanding how people use Anytype helps us improve the product. This version of Anytype includes the analytics code that protects your privacy. It doesn't record the actual document's content but still allows us to understand how you use Anytype. Stay subscribed to our mailing list, as we will soon announce a new release that enables you to opt-out." />
<Button text="Start" className="orange" onClick={this.onSubmit} />
</div>
);
};
onSubmit (e: any) {
e.preventDefault();
this.props.history.push('/auth/select');
};
resize () {
let node = $(ReactDOM.findDOMNode(this));
node.css({ marginTop: -node.outerHeight() / 2 });
};
};
export default PageAuthNotice;

View file

@ -0,0 +1,87 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Title, Label, Error, Input, Button, HeaderAuth as Header, FooterAuth as Footer } from 'ts/component';
const $ = require('jquery');
const SIZE = 6;
interface Props extends RouteComponentProps<any> {};
interface State {
error: string;
code: string;
};
class PageAuthPinConfirm extends React.Component<Props, State> {
refObj: any = {};
state = {
code: '',
error: ''
};
constructor (props: any) {
super(props);
this.onChange = this.onChange.bind(this);
};
render () {
const { error } = this.state;
let inputs = [];
for (let i = 1; i <= SIZE; ++i) {
inputs.push({ id: i });
};
return (
<div>
<div className="cover c3" />
<Header />
<Footer />
<div className="frame">
<Title text="Confirm pin code" />
<Label text="This is one password you need to remember. You will need this password to login on this device." />
<Error text={error} />
{inputs.map((item, i) => (
<Input ref={(ref: any) => this.refObj[item.id] = ref} maxLength={1} key={i} onKeyUp={(e: any) => { this.onChange(e, item.id); }} />
))}
</div>
</div>
);
};
componentDidMount () {
this.refObj[1].focus();
};
onChange (e: any, id: number) {
let { code } = this.state;
code += this.refObj[id].getValue();
this.setState({ code: code });
this.refObj[id].setType('password');
if (this.refObj[id + 1]) {
this.refObj[id + 1].focus();
};
if (code.length == SIZE) {
this.props.history.push('/auth/setup');
};
};
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('.frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
};
};
export default PageAuthPinConfirm;

View file

@ -0,0 +1,87 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Title, Label, Error, Input, Button, HeaderAuth as Header, FooterAuth as Footer } from 'ts/component';
const $ = require('jquery');
const SIZE = 6;
interface Props extends RouteComponentProps<any> {};
interface State {
error: string;
code: string;
};
class PageAuthPinSelect extends React.Component<Props, State> {
refObj: any = {};
state = {
code: '',
error: ''
};
constructor (props: any) {
super(props);
this.onChange = this.onChange.bind(this);
};
render () {
const { error } = this.state;
let inputs = [];
for (let i = 1; i <= SIZE; ++i) {
inputs.push({ id: i });
};
return (
<div>
<div className="cover c3" />
<Header />
<Footer />
<div className="frame">
<Title text="Choose pin code" />
<Label text="This is one password you need to remember. You will need this password to login on this device." />
<Error text={error} />
{inputs.map((item, i) => (
<Input ref={(ref: any) => this.refObj[item.id] = ref} maxLength={1} key={i} onKeyUp={(e: any) => { this.onChange(e, item.id); }} />
))}
</div>
</div>
);
};
componentDidMount () {
this.refObj[1].focus();
};
onChange (e: any, id: number) {
let { code } = this.state;
code += this.refObj[id].getValue();
this.setState({ code: code });
this.refObj[id].setType('password');
if (this.refObj[id + 1]) {
this.refObj[id + 1].focus();
};
if (code.length == SIZE) {
this.props.history.push('/auth/pin-confirm');
};
};
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('.frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
};
};
export default PageAuthPinSelect;

View file

@ -38,25 +38,21 @@ class PageAuthRegister extends React.Component<Props, State> {
const { error, preview, name } = this.state;
return (
<div>
<div className="cover c1" />
<div id="frame" className="frame">
<Title text="Get Anytype ID" />
<Label text="This ID and your data will be encrypted and stored locally, and only you will have keys to decrypt it. " />
<Error text={error} />
<div className="frame">
<Title text="Add your name and profile picture" />
<Error text={error} />
<div className="inputWithImage">
<div className="fileWrap">
<IconUser name={name || 'T'} icon={preview} />
<Input ref={(ref: any) => this.fileRef = ref} id="file" type="file" onChange={this.onFileChange} />
</div>
<Input ref={(ref: any) => this.nameRef = ref} placeHolder="Type your name" value={name} onKeyUp={this.onNameChange} />
<div className="inputWithImage">
<div className="fileWrap">
<IconUser name={name || 'T'} icon={preview} />
<Input ref={(ref: any) => this.fileRef = ref} id="file" type="file" onChange={this.onFileChange} />
</div>
<Input ref={(ref: any) => this.nameRef = ref} placeHolder="Type your name" value={name} onKeyUp={this.onNameChange} />
</div>
<div className="buttons">
<Button text="Get ID" className="orange" onClick={this.onSubmit} />
<Button text="Back" className="grey" onClick={this.onCancel} />
</div>
<div className="buttons">
<Button text="Create profile" className="orange" onClick={this.onSubmit} />
<Button text="Back" className="grey" onClick={this.onCancel} />
</div>
</div>
);
@ -85,9 +81,8 @@ class PageAuthRegister extends React.Component<Props, State> {
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('#frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
node.css({ marginTop: -node.outerHeight() / 2 });
};
};

View file

@ -1,25 +1,15 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Title, Label, Error, Input, Button } from 'ts/component';
import { Title, Label, Error, Input, Button, HeaderAuth as Header, FooterAuth as Footer } from 'ts/component';
const $ = require('jquery');
interface Props extends RouteComponentProps<any> {
};
interface State {
error: string;
};
interface Props extends RouteComponentProps<any> {};
interface State {};
class PageAuthSelect extends React.Component<Props, State> {
codeRef: any;
state = {
error: ''
};
constructor (props: any) {
super(props);
@ -28,18 +18,19 @@ class PageAuthSelect extends React.Component<Props, State> {
};
render () {
const { error } = this.state;
return (
<div>
<div className="cover c1" />
<div id="frame" className="frame">
<div className="cover c3" />
<Header />
<Footer />
<div className="frame">
<Title text="Organize everything" />
<Label text="With Anytype you can write notes and documents, manage tasks, share files and save important content from the web." />
<div className="buttons">
<Button text="Get your ID" type="input" className="orange" onClick={this.onRegister} />
<Button text="Sign in" type="input" className="grey" onClick={this.onLogin} />
<Button text="Login" type="input" className="orange" onClick={this.onLogin} />
<Button text="Sign up" type="input" className="grey" onClick={this.onRegister} />
</div>
</div>
</div>
@ -49,7 +40,7 @@ class PageAuthSelect extends React.Component<Props, State> {
onRegister (e: any) {
e.preventDefault();
this.props.history.push('/auth/register');
this.props.history.push('/auth/code');
};
onLogin (e: any) {
@ -60,7 +51,7 @@ class PageAuthSelect extends React.Component<Props, State> {
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('#frame');
let frame = node.find('.frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
};

View file

@ -0,0 +1,73 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Title, Label, Error, Input, Button, Smile } from 'ts/component';
const $ = require('jquery');
interface Props extends RouteComponentProps<any> {};
interface State {
icon: number;
};
const Icons: number[] = [
12, 1230, 1, 130, 2, 230, 3, 330, 4, 430, 5, 530, 6, 630, 7, 730, 8, 830, 9, 930, 10, 1030, 11, 1130
];
class PageAuthSetup extends React.Component<Props, State> {
i: number = 0;
t: number = 0;
state = {
icon: 0
};
render () {
const { icon } = this.state;
return (
<div>
<div className="cover c3" />
<div className="logo" />
<div className="copy">2018, Anytype</div>
<div className="frame">
<Smile icon={':clock' + Icons[icon] + ':'} size={36} />
<Title text="Setting up the wallet..." />
</div>
</div>
);
};
componentDidMount () {
this.i = window.setInterval(() => {
let { icon } = this.state;
icon++;
if (icon >= Icons.length) {
icon = 0;
};
this.setState({ icon: icon });
}, 1000);
this.t = window.setTimeout(() => {
this.props.history.push('/auth/account-select');
}, 3000);
};
componentWillUnmount () {
clearInterval(this.i);
clearTimeout(this.t);
};
resize () {
let node = $(ReactDOM.findDOMNode(this));
let frame = node.find('.frame');
frame.css({ marginTop: -frame.outerHeight() / 2 });
};
};
export default PageAuthSetup;

View file

@ -55,13 +55,7 @@ class IconUser extends React.Component<Props, {}> {
if (!s) {
return '';
};
let a = s.split(' ') || [];
a = a.slice(0, 2);
for (let i = 0; i < a.length; ++i) {
a[i] = a[i].substr(0, 1);
};
return a.join('');
return s.trim().substr(0, 1);
};
onMouseEnter (e: any) {

View file

@ -25,6 +25,7 @@ interface Props {
interface State {
value: string;
selected: boolean;
type: string;
};
class Input extends React.Component<Props, State> {
@ -36,7 +37,8 @@ class Input extends React.Component<Props, State> {
state = {
value: '',
selected: false
selected: false,
type: ''
};
constructor (props: any) {
@ -50,8 +52,9 @@ class Input extends React.Component<Props, State> {
};
render () {
const { id, type, name, placeHolder, className, autoComplete, readOnly, maxLength, multiple, accept } = this.props;
const { id, name, placeHolder, className, autoComplete, readOnly, maxLength, multiple, accept } = this.props;
let type: string = this.state.type || this.props.type;
let cn = [ 'input', 'input-' + type ];
if (className) {
cn.push(className);
@ -84,6 +87,7 @@ class Input extends React.Component<Props, State> {
componentDidMount () {
this.setValue(this.props.value);
this.setState({ type: this.props.type });
};
componentDidUpdate () {
@ -144,6 +148,10 @@ class Input extends React.Component<Props, State> {
return this.state.value;
};
setType (v: string) {
this.setState({ type: v });
};
};
export default Input;

View file

@ -0,0 +1,27 @@
import * as React from 'react';
import { Emoji } from 'emoji-mart';
interface Props {
icon: string;
size: number;
};
class Smile extends React.Component<Props, {}> {
private static defaultProps = {
size: 18
};
render() {
const { icon, size } = this.props;
return (
<div className="smile" {...this.props}>
{icon ? <Emoji emoji={icon} set="apple" size={size} /> : ''}
</div>
);
};
};
export default Smile;