update: app model, login & authframes

This commit is contained in:
srgooglo 2020-08-25 02:55:20 +02:00
parent 90bbda47b2
commit fc5e4a0106
25 changed files with 1674 additions and 123 deletions

2
.gitignore vendored
View File

@ -13,4 +13,4 @@
/src/.umi-production /src/.umi-production
/src/.umi-test /src/.umi-test
/.env.local /.env.local
api/

View File

@ -1,25 +1,7 @@
import { defineConfig } from 'umi'; import { defineConfig } from 'umi';
const Path = require('path');
const { resolve } = require('path'); const { resolve } = require('path');
const themePth = require('./src/theme/index.js')
const lessToJs = require('less-vars-to-js');
const fs = require('fs');
const AntDesignThemePlugin = require('antd-theme-webpack-plugin');
const options = {
antDir: Path.join(__dirname, './node_modules/antd'),
stylesDir: Path.join(__dirname, './src/styles'),
varFile: Path.join(__dirname, './src/styles/variables.less'),
themeVariables: ['@primary-color'],
indexFileName: 'index.html'
}
const themePlugin = new AntDesignThemePlugin(options);
const convToVars = file => lessToJs(fs.readFileSync(Path.join(__dirname, file), 'utf8'))
export default defineConfig({ export default defineConfig({
hash: false, hash: false,
ignoreMomentLocale: true, ignoreMomentLocale: true,
@ -33,7 +15,7 @@ export default defineConfig({
}, },
alias: { alias: {
antd: resolve(__dirname, './node_modules/antd'), antd: resolve(__dirname, './node_modules/antd'),
api: resolve(__dirname, './node_modules/@ragestudio/ycorejs-lib'), api: resolve(__dirname, './node_modules/@ragestudio/ycorejs-lib'), // ./api
globals: resolve(__dirname, './globals'), globals: resolve(__dirname, './globals'),
core: resolve(__dirname, './src/core'), core: resolve(__dirname, './src/core'),
theme: resolve(__dirname, './src/theme'), theme: resolve(__dirname, './src/theme'),

View File

@ -4,4 +4,5 @@ export default {
logout: 'POST /logout', logout: 'POST /logout',
get_data: 'POST /get-user-data', get_data: 'POST /get-user-data',
profileData: 'POST /early_user'
}; };

View File

@ -40,7 +40,7 @@ module.exports = {
include: [/.*/] include: [/.*/]
} }
], ],
// Default Behaviors // Default Behaviors
defaults: { defaults: {
verbosity: false, verbosity: false,
@ -50,14 +50,16 @@ module.exports = {
render_pagetransition_preset: 'moveToRightScaleUp', render_pagetransition_preset: 'moveToRightScaleUp',
feed_autorefresh: false, feed_autorefresh: false,
},
post_maxlenght: '512', stricts: {
post_catchlimit: '20', post_maxlenght: '512',
post_hidebar: true, post_catchlimit: '20',
post_hidebar: true,
// In KB // In KB
api_maxpayload: '101376', api_maxpayload: '101376',
api_maxovertick: 10, api_maxovertick: 10,
} }
}; };

View File

@ -3,7 +3,7 @@
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4", "UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
"title": "Comty™", "title": "Comty™",
"DevBuild": true, "DevBuild": true,
"version": "0.8.08", "version": "0.8.22",
"stage": "dev-pre", "stage": "dev-pre",
"description": "", "description": "",
"author": "RageStudio", "author": "RageStudio",
@ -36,6 +36,7 @@
"@material-ui/core": "^4.9.9", "@material-ui/core": "^4.9.9",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@ragestudio/ycorejs-lib": "^0.1.22", "@ragestudio/ycorejs-lib": "^0.1.22",
"@steveeeie/react-page-transition": "^1.2.0",
"antd": "^4.5.1", "antd": "^4.5.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",

View File

@ -4,7 +4,7 @@ import styles from './Loader.less'
const Loader = (loading) => { const Loader = (loading) => {
return ( return (
<div className={classNames(styles.wrapper, {[styles.end]: !loading.spinning })}> <div className={classNames(styles.wrapper, {[styles.end]: !loading })}>
<span>Loading... </span> <span>Loading... </span>
<div <div
className={styles.newloader} className={styles.newloader}

View File

@ -0,0 +1,322 @@
import React from 'react'
import * as antd from 'antd'
import * as core from 'core'
import * as Icons from 'components/Icons'
import styles from './index.less'
import { connect } from 'umi'
import { stricts } from 'config'
import { settings, newSetting } from 'core/libs/settings'
import $ from 'jquery'
@connect(({ app }) => ({ app }))
class PostCreator extends React.PureComponent {
constructor(props) {
super(props),
this.state = {
maxFileSize: stricts.api_maxpayload,
maxTextLenght: stricts.post_maxlenght,
renderValid: false,
loading: false,
textLenght: stricts.post_maxlenght,
rawText: '',
posting: false,
postingResult: false,
shareWith: 'any',
uploader: false,
uploaderFile: null,
uploaderFileOrigin: null,
},
window.PostCreatorComponent = this
}
dropRef = React.createRef()
ToogleUploader() {
this.setState({ uploader: !this.state.uploader })
}
handleDeleteFile = () => {
this.setState({ uploaderFile: null })
}
handleFileUpload = info => {
if (info.file.status === 'uploading') {
this.setState({ loading: true })
}
if (info.file.status === 'done') {
this.setState({ uploaderFileOrigin: info.file.originFileObj, uploader: false })
core.getBase64(info.file.originFileObj, fileURL => {
this.setState({ uploaderFile: fileURL, loading: false })
})
}
}
beforeUpload = file => {
const filter =
file.type === 'image/jpeg' ||
file.type === 'audio/mp3' ||
file.type === 'audio/wav' ||
file.type === 'audio/ogg' ||
file.type === 'image/png' ||
file.type === 'image/jpg' ||
file.type === 'image/gif' ||
file.type === 'video/mp4'
if (!filter) {
antd.message.error(`${file.type} This file is not valid!`)
}
const maxsize = file.size / 1024 / 1024 < stricts.api_maxpayload
if (!maxsize) {
antd.message.error(
`Image must smaller than ${stricts.api_maxpayload} KB!`
)
}
return filter && maxsize
}
handleChanges = ({ target: { value } }) => {
this.setState({
rawText: value,
textLenght: this.state.maxTextLenght - value.length,
})
}
handleKeysProgressBar() {
return this.state.textLenght <= (this.state.maxTextLenght / 100) * 30? 'exception' : 'active'
}
handleDragIn = e => {
e.preventDefault()
e.stopPropagation()
this.state.uploader? this.setState({ uploader: true }) : null
}
handleDragOut = e => {
e.preventDefault()
e.stopPropagation()
this.state.uploader? null : this.setState({ uploader: false })
}
componentDidMount() {
// Validate for render
if (this.props.app.userData) {
this.setState({renderValid: true})
}
// const _this = this
// $('body').bind('paste', function(je) {
// var e = je.originalEvent
// for (var i = 0; i < e.clipboardData.items.length; i++) {
// var item = e.clipboardData.items[i]
// if (item.type.indexOf('image') != -1) {
// //item.
// let a;
// a = item.getAsFile()
// _this.setState({ uploaderFileOrigin: a })
// core.ReadFileAsB64(a, res => {
// _this.setState({ uploaderFile: res })
// })
// } else {
// // ignore not images
// }
// }
// })
// let div = this.dropRef.current
// div.addEventListener('dragenter', this.handleDragIn)
// div.addEventListener('dragleave', this.handleDragOut)
}
componentWillUnmount() {
// let div = this.dropRef.current
// div.removeEventListener('dragenter', this.handleDragIn)
// div.removeEventListener('dragleave', this.handleDragOut)
}
canPost() {
const isTypedSomething = this.state.textLenght < this.state.maxTextLenght
const isUploadedFile = this.state.uploaderFile ? true : false
return isUploadedFile || isTypedSomething
}
render() {
const { userData } = this.props.app
const { textLenght, uploaderFile } = this.state
const GetPostPrivacy = {
bool: (e) => {
switch (e) {
case 'any':
return '0'
case 'only_followers':
return '1'
case 'only_follow':
return '2'
case 'private':
return '3'
default:
return '0'
}
},
decorator: (e) => {
switch (e) {
case 'any':
return <span><Icons.GlobalOutlined /> Share with everyone</span>
case 'only_follow':
return <span><Icons.TeamOutlined /> Share with people I follow</span>
case 'only_followers':
return <span><Icons.UsergroupAddOutlined /> Share with people follow me</span>
case 'private':
return <span><Icons.EyeInvisibleOutlined /> Dont share, only me</span>
default:
return <span>Unknown</span>
}
},
}
const shareOptionsMenu = (
<antd.Menu onClick={key => this.setState({ shareWith: key })}>
<antd.Menu.Item key="any">
{GetPostPrivacy.decorator('any')}
</antd.Menu.Item>
<antd.Menu.Item key="only_follow">
{GetPostPrivacy.decorator('only_follow')}
</antd.Menu.Item>
<antd.Menu.Item key="only_followers">
{GetPostPrivacy.decorator('only_followers')}
</antd.Menu.Item>
<antd.Menu.Item key="private">
{GetPostPrivacy.decorator('private')}
</antd.Menu.Item>
</antd.Menu>
)
const PostCreator_Uploader = () => {
return(
<div className={styles.uploader}>
<antd.Upload.Dragger
multiple={false}
listType="picture"
showUploadList={false}
beforeUpload={this.beforeUpload}
onChange={this.handleFileUpload}
>
<Icons.CloudUploadOutlined />
<span>Drop your file here o click for upload</span>
</antd.Upload.Dragger>
</div>
)
}
const PostCreator_InputText = () => {
return(
<>
<div className={styles.titleAvatar}>
<img src={userData.avatar} />
</div>
<antd.Input.TextArea
disabled={this.state.posting ? true : false}
onPressEnter={this.handlePublishPost}
value={this.state.rawText}
autoSize={{ minRows: 3, maxRows: 5 }}
dragable="false"
placeholder="What are you thinking?"
onChange={this.handleChanges}
allowClear
maxLength={this.state.maxTextLenght}
rows={8}
/>
<div>
<antd.Button
disabled={this.state.posting ? true : !this.canPost()}
onClick={this.handlePublishPost}
type="primary"
icon={
this.state.postingResult ? (
<Icons.CheckCircleOutlined />
) : this.state.posting ? (
<Icons.LoadingOutlined />
) : (
<Icons.ExportOutlined />
)
}
/>
</div>
</>
)
}
const PostCreatorComponent = () => {
return(
<>
<div ref={this.dropRef} className={styles.inputWrapper}>
{this.state.uploader ? <PostCreator_Uploader /> : <PostCreator_InputText /> }
</div>
<div className={styles.progressHandler}>
<antd.Progress
className={
this.state.posting
? styles.proccessUnset
: textLenght < 512
? styles.proccessSet
: styles.proccessUnset
}
percent={((textLenght / this.state.maxTextLenght) * 100).toFixed(2)}
status={this.handleKeysProgressBar()}
strokeWidth="4px"
showInfo={false}
/>
</div>
{uploaderFile ? this.renderPostPlayer(uploaderFile) : null}
<div className={styles.postExtra}>
<antd.Button
styles={this.state.uploader ? { fontSize: '20px' } : null}
type="ghost"
onClick={() => this.ToogleUpload()}
>
{this.state.uploader ? (
<Icons.Cancel />
) : (
<Icons.AddCircle />
)}
</antd.Button>
<antd.Button type="ghost" onClick={() => null}>
<Icons.Tune />
</antd.Button>
<antd.Dropdown overlay={shareOptionsMenu}>
<a
className={styles.shareWith}
onClick={e => e.preventDefault()}
>
{GetPostPrivacy.decorator(this.state.shareWith)}
</a>
</antd.Dropdown>
</div>
</>
)
}
const PostCreator_Invalid = () => {
return(
<div>
<h3>This component cant be displayed!</h3>
<antd.Skeleton active />
</div>
)
}
return (
<div className={styles.cardWrapper}>
<antd.Card bordered="false">
{ this.state.renderValid? <PostCreatorComponent /> : <PostCreator_Invalid /> }
</antd.Card>
</div>
)
}
}
export default PostCreator

View File

@ -0,0 +1,315 @@
@import '~theme/index.less';
.cardWrapper {
// box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
border-radius: 7px;
max-width: 510px;
min-width: 265px;
width: auto;
margin: 7px auto 50px auto;
:global {
textarea {
font-weight: 500;
resize: none;
outline: none !important;
border: 0 !important;
}
textarea:focus {
outline: none !important;
border: 0 !important;
}
textarea:hover {
outline: none !important;
border: 0 !important;
}
.ant-card-meta-detail>div:not(:last-child) {
margin: 0
}
.ant-card {
border-radius: 7px;
border: 0;
border-top: 1px solid #4646460c;
}
.ant-card-body {
padding: 5px 15px 5px 15px;
}
.ant-card-actions {
border-top: 0;
background: #EBEBEB;
opacity: 0;
height: 30px;
position: relative;
transition: opacity 150ms linear, position 150ms linear, transform 150ms linear;
border-radius: 0 0 10px 10px;
}
.ant-card-actions:hover {
opacity: 1;
transform: translate(0, 15px);
transition: opacity 150ms linear, position 150ms linear, transform 150ms linear;
}
.ant-card-actions>li {
margin: -20px 0 0 0;
border-right: 0;
i {
vertical-align: middle;
height: 40px;
width: 40px;
background-color: #fff;
border-radius: 24px;
}
svg {
height: 20px;
width: 20px;
height: 100%;
vertical-align: middle;
}
}
}
}
.titleAvatar {
width: 45px;
height: 45px;
display: flex;
:global {
img {
width: 45px;
height: 45px;
border-radius: 12px;
}
}
}
.inputWrapper {
display: flex;
z-index: 10;
position: relative;
width: 100%;
padding: 18px 7px 0 7px;
transition: height 150ms linear;
:global {
.ant-btn-primary {
z-index: 10;
position: relative;
border-radius: 0 10px 10px 0;
height: 100%;
vertical-align: bottom;
border: none;
box-shadow: none;
}
.ant-input {
z-index: 10;
position: relative;
border-color: transparent !important;
box-shadow: none;
border-radius: 3px 0 0 0;
height: 100%;
padding: 5px 10px 5px 10px;
transition: height 150ms linear;
width: 100%;
}
.ant-input:hover {
border-color: #1890ff;
}
.ant-input-affix-wrapper {
height: 100%;
}
}
}
.progressHandler {
z-index: 10;
position: relative;
margin: 0 7px 0 7px;
:global {
.ant-progress-bg {
border-radius: 0 0 10px 10px;
}
.ant-progress-inner {
border-radius: 0 0 14px 14px;
width: calc(100% - 32px);
vertical-align: top;
}
}
}
.postExtra {
width: 100%;
height: 100%;
position: relative;
margin: 0 0 40px 0;
.shareWith {
color: rgb(53, 53, 53);
float: right;
font-size: 11px;
line-height: 30px;
}
:global {
.MuiSvgIcon-root {
width: 1em;
height: 1em;
display: inline-block;
font-size: 18px;
transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
flex-shrink: 0;
margin: 8px;
line-height: 1px;
user-select: none;
}
.ant-btn .anticon {
transition: margin-left 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
margin: 8px;
}
.ant-btn {
width: 35px;
height: 35px;
float: left;
padding: 0;
border-radius: 11px;
margin: 0 10px;
background-color: #eeeeee;
border-color: transparent;
}
.ant-btn:hover {
border-color: transparent;
}
}
}
.uploader {
display: flex;
position: relative;
border-radius: 10px;
z-index: 30;
width: 100%;
height: 100%;
span {
width: 100%;
}
:global {
.ant-upload.ant-upload-drag {
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 12px;
transition: border-color 0.3s;
}
.anticon svg {
display: inline-block;
font-size: 30px;
}
}
}
.imagePreviewWrapper {
position: relative;
width: 100%;
height: 100%;
// top: -100px;
margin: 0 0 15px 0;
background-color: #eeeeee;
.imagePreview {
z-index: 5;
position: relative;
width: 50%;
margin: auto;
border-radius: 8px;
transition: all 150ms linear;
img {
width: 100%;
border: 0.5px rgba(56, 56, 56, 0.459) solid;
}
video {
width: 100%;
border: 0.5px rgba(56, 56, 56, 0.459) solid;
}
transition: all 150ms linear;
}
.imageOverlay {
z-index: 10;
position: relative;
opacity: 0;
transition: all 150ms linear;
margin: auto;
}
}
.imagePreviewWrapper:hover .imagePreview {
opacity: 0.5;
transition: all 150ms linear;
}
.imagePreviewWrapper:hover .imageOverlay {
opacity: 1;
transition: all 150ms linear;
}
.proccessUnset {
opacity: 0;
transition: opacity 250ms linear;
animation: proccessUnset 250ms linear;
}
.proccessSet {
transition: opacity 250ms linear;
animation: proccessSet 250ms linear;
}
@keyframes proccessSet {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes proccessUnset {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.fontct {
font-family: "Poppins", sans-serif;
}

View File

@ -15,6 +15,7 @@ import UserBadges from './UserBadges'
// Post Components // Post Components
import MediaPlayer from './MediaPlayer' import MediaPlayer from './MediaPlayer'
import PostCreator from './PostCreator'
// Mix & Export all // Mix & Export all
export { export {
@ -25,5 +26,6 @@ export {
UserBadges, UserBadges,
PageTransition, PageTransition,
MyLayout, MyLayout,
Loader Loader,
PostCreator
} }

View File

@ -1,3 +1,6 @@
export * from './session'; export * from './router'
export * from './user';
export * from './router' import * as user from './user'
import * as session from './session'
export { user, session }

View File

@ -11,18 +11,18 @@ import { history } from 'umi';
* Specify the paths of the files, in this case it is pointing to the root * Specify the paths of the files, in this case it is pointing to the root
*/ */
export const router = { export const router = {
go: e => {
goTo.element('primaryContent');
router.push(e);
},
push: e => { push: e => {
history.push({ history.push({
pathname: `${e}`, pathname: `${e}`,
}); });
}, },
goprofile: () => { go: e => {
router.push(e);
goTo.element('primaryContent'); goTo.element('primaryContent');
},
goprofile: () => {
router.push(`/@${e}`); router.push(`/@${e}`);
goTo.element('primaryContent');
}, },
}; };

View File

@ -1,46 +1,38 @@
/**
*
* @param {HTMLTableElement} session - Callback / Credentials
* @returns {callback} make an API call to verify credentials
* @throws {NotFoundError} show error when credentials were not correctly put
* @async
*
*
*/
import endpoints from 'config/endpoints'; import endpoints from 'config/endpoints';
import { v3_model } from 'core/libs'; import { v3_model } from 'core/libs';
// @param {Array} payload callback - check the information and if it is correct give access
function auth(payload, callback) { function auth(payload, callback) {
if (!payload) return false; if (!payload) return false;
const { username, password, server_key } = payload; const { username, password, server_key } = payload;
if (username && password) { if (username && password) {
const frame = { username: atob(username), password: atob(password) }
v3_model.api_request( v3_model.api_request(
{ {
body: frame,
endpoint: endpoints.auth, endpoint: endpoints.auth,
serverKey: server_key, serverKey: server_key,
verbose: true, verbose: true,
}, },
(err, res) => { (err, res) => {
console.log(err, res); return callback(err, res);
}, },
); );
return callback(false, true);
} else { } else {
const res = { status: 100, message: 'Invalid Credentials!' }; const res = { status: 100, message: 'Invalid Credentials!' };
return callback(res, false); return callback(res, false);
} }
} }
async function deauth() {} function deauth() {
}
// check the information and if it is correct give access
const backup = { const backup = {
get: () => {}, get: () => {},
set: () => {}, set: () => {},
}; };
export { auth, deauth, backup }; export { auth, deauth, backup };

View File

@ -7,21 +7,28 @@ const set = {
}, },
}; };
const get = { export const get = {
data: parms => { data: (parms, callback) => {
if (!parms) return false; if (!parms) return false;
const { id, type } = parms; const { id, access_token, serverKey, fetch } = parms;
if (!id) { let req = {
fetch: fetch? fetch : 'user_data'
}
if (!id || !access_token) {
// core get id data from current session // core get id data from current session
} }
v3_model.api_request( v3_model.api_request(
{ {
body: {user_id: id, fetch: req.fetch},
serverKey: serverKey,
userToken: access_token,
endpoint: endpoints.get_data, endpoint: endpoints.get_data,
verbose: true, verbose: true,
}, },
(err, res) => { (err, res) => {
console.log(err, res); return callback(err, res)
}, },
); );
}, },
@ -33,17 +40,32 @@ const get = {
// core get id data from current session // core get id data from current session
} }
}, },
profileData: parms => { profileData: (parms, callback) => {
if (!parms) return false; if (!parms) return false
const { id } = parms;
if (!id) { const { username } = parms
// core get id data from current session
if (username) {
v3_model.api_request(
{
body: { username },
endpoint: endpoints.profileData,
verbose: true,
},
(err, res) => {
err? console.error(err) : null
return callback(false, res);
},
);
} else {
const res = { status: 105, message: 'Invalid Username!' };
return callback(res, false);
} }
}, },
}; };
const actions = { export const actions = {
block: parms => { block: parms => {
if (!parms) return false; if (!parms) return false;
const { id, toID } = parms; const { id, toID } = parms;
@ -58,8 +80,3 @@ const actions = {
}, },
}; };
export {
//set
get,
actions,
};

View File

@ -1,13 +1,10 @@
import { lib, v3_request } from 'api'; import { lib, v3_request } from 'api';
import endpoints_list from 'config/endpoints'; import endpoints_list from 'config/endpoints';
import { app_config } from 'config'; import { app_config } from 'config';
import * as core from 'core'
import { connect } from 'dva';
const { api_prefix } = app_config; const { api_prefix } = app_config;
const { uri_resolver } = lib; const { uri_resolver } = lib;
async function compileURI(e, callback) { async function compileURI(e, callback) {
const resolvers = await uri_resolver(); const resolvers = await uri_resolver();
const prefix = resolvers[api_prefix]; const prefix = resolvers[api_prefix];
@ -15,7 +12,6 @@ async function compileURI(e, callback) {
let final = null; let final = null;
let url; let url;
let method; let method;
const endpointSplit = e.split(' '); const endpointSplit = e.split(' ');
if (endpointSplit.length === 2) { if (endpointSplit.length === 2) {
method = endpointSplit[0]; method = endpointSplit[0];

View File

@ -1,4 +1,4 @@
import React, { PureComponent, Fragment } from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { Loader } from 'components' import { Loader } from 'components'
@ -8,39 +8,44 @@ import { queryLayout } from 'core'
import config from 'config' import config from 'config'
import PrimaryLayout from './PrimaryLayout' import PrimaryLayout from './PrimaryLayout'
import PublicLayout from './PublicLayout'
import './BaseLayout.less' import './BaseLayout.less'
const LayoutMap = { const LayoutMap = {
primary: PrimaryLayout primary: PrimaryLayout,
// public: PublicLayout, public: PublicLayout,
} }
@withRouter @withRouter
@connect(({ app, loading }) => ({ app, loading })) @connect(({ app, loading }) => ({ app, loading }))
class BaseLayout extends PureComponent { class BaseLayout extends React.Component {
previousPath = '' previousPath = ''
renderLoading = true
render() { render() {
const { loading, children, location } = this.props const { loading, children, location } = this.props
const Container = LayoutMap[queryLayout(config.layouts, location.pathname)] const Container = LayoutMap[queryLayout(config.layouts, location.pathname)]
const currentPath = location.pathname + location.search const currentPath = location.pathname + location.search
if (currentPath !== this.previousPath) { if (currentPath !== this.previousPath) {
NProgress.start() NProgress.start()
this.renderLoading = true
} }
if (!loading.global) { if (!loading.global) {
NProgress.done() NProgress.done()
this.previousPath = currentPath this.previousPath = currentPath
this.renderLoading = false
} }
return ( return (
<Fragment> <React.Fragment>
<Helmet> <Helmet>
<title>{config.app_config.siteName}</title> <title>{config.app_config.siteName}</title>
</Helmet> </Helmet>
{Loader(loading)} {Loader(this.renderLoading)}
<Container>{children}</Container> <Container>{children}</Container>
</Fragment> </React.Fragment>
) )
} }
} }

View File

@ -5,7 +5,6 @@ import PropTypes from 'prop-types'
import {withRouter, connect} from 'umi' import {withRouter, connect} from 'umi'
import { import {
MyLayout, MyLayout,
PageTransition,
} from 'components' } from 'components'
import { enquireScreen, unenquireScreen } from 'enquire-js' import { enquireScreen, unenquireScreen } from 'enquire-js'
import store from 'store' import store from 'store'
@ -103,17 +102,13 @@ class PrimaryLayout extends React.Component {
<antd.Layout id="app" className={isActive(currentTheme['darkmode'])? "dark_mode" : null }> <antd.Layout id="app" className={isActive(currentTheme['darkmode'])? "dark_mode" : null }>
<Sider {...SiderProps} /> <Sider {...SiderProps} />
<div className={styles.primary_layout_container}> <div className={styles.primary_layout_container}>
<PageTransition
preset="moveToRightScaleUp"
transitionKey={location.pathname}
>
<Content <Content
id="primaryContent" id="primaryContent"
className={styles.primary_layout_content} className={styles.primary_layout_content}
> >
{children? children : null} {children? children : null}
</Content> </Content>
</PageTransition>
</div> </div>
<Overlay {...OverlayProps} /> <Overlay {...OverlayProps} />
</antd.Layout> </antd.Layout>

View File

@ -0,0 +1,67 @@
import React from 'react'
import store from 'store'
import {
MyLayout,
PageTransition,
} from 'components'
import { enquireScreen, unenquireScreen } from 'enquire-js'
import classnames from 'classnames'
import * as antd from 'antd'
import * as Icons from 'components/Icons'
import styles from './PrimaryLayout.less'
const { Content } = antd.Layout
export default class PublicLayout extends React.Component {
constructor(props) {
super(props)
window.PrimaryComponent = this
this.state = {
isMobile: false,
}
}
componentDidMount() {
this.enquireHandler = enquireScreen(mobile => {
const { isMobile } = this.state
if (isMobile !== mobile) {
this.setState({
isMobile: mobile,
})
store.set('mobile_src', mobile)
}
})
}
componentWillUnmount() {
unenquireScreen(this.enquireHandler)
}
render() {
const { children } = this.props
const { isMobile } = this.state
return (
<React.Fragment>
<antd.Layout id="publicLayout" className={classnames(styles.primary_layout, {[styles.mobile]: isMobile})}>
<div className={styles.primary_layout_container}>
<PageTransition
preset="moveToRightScaleUp"
transitionKey={window.location.pathname}
>
<Content
id="publicContent"
className={styles.primary_layout_content}
>
{children}
</Content>
</PageTransition>
</div>
</antd.Layout>
</React.Fragment>
)
}
}

View File

@ -3,10 +3,9 @@ import store from 'store';
import { pathMatchRegexp, queryLayout } from 'core'; import { pathMatchRegexp, queryLayout } from 'core';
import { app_config } from 'config'; import { app_config } from 'config';
import keys from 'config/app_keys'; import keys from 'config/app_keys';
import * as core from 'core'; import { router } from 'core/cores';
import { session } from 'core/cores';
import verbosity from 'core/libs/verbosity' import verbosity from 'core/libs/verbosity'
import { theme } from 'core/libs/style' import { notify } from 'core/libs/interface/notify'
export default { export default {
namespace: 'app', namespace: 'app',
@ -17,7 +16,7 @@ export default {
ng_services: false, ng_services: false,
session_valid: false, session_valid: false,
session_token: null, session_token: sessionStorage.getItem('session'),
session_data: null, session_data: null,
session_uuid: null, session_uuid: null,
@ -63,30 +62,31 @@ export default {
effects: { effects: {
*query({ payload }, { call, put, select }) { *query({ payload }, { call, put, select }) {
const service = yield select(state => state.app.service_valid); const service = yield select(state => state.app.service_valid);
const session = yield select(state => state.app.session_valid);
yield put({ type: 'updateFrames' })
if (!service) { if (!service) {
console.error('❌ Cannot connect with validate session service!'); console.error('❌ Cannot connect with validate session service!');
return yield put({
type: 'updateState',
payload: { service_valid: false },
});
} }
if (session) {
if (pathMatchRegexp(['/', '/login'], window.location.pathname)) {
app.router.push({ pathname: `${app_config.MainPath}` });
}
return true; // if (session) {
} else if ( // if (pathMatchRegexp(['/', '/login'], window.location.pathname)) {
!pathMatchRegexp(['', '/login'], window.location.pathname) && // app.router.push({ pathname: `${app_config.MainPath}` });
queryLayout(config.layouts, window.location.pathname) !== 'public' // }
) {
if (validBackup == true) { // return true;
// logout normal // } else if (
} else { // !pathMatchRegexp(['', '/login'], window.location.pathname) &&
core.router.push({ pathname: '/login' }); // queryLayout(config.layouts, window.location.pathname) !== 'public'
} // ) {
} // if (validBackup == true) {
// // logout normal
// } else {
// core.router.push({ pathname: '/login' });
// }
// }
}, },
*update({ payload }, { call, put, select }) { *update({ payload }, { call, put, select }) {
const session = yield select(state => state.app.session_valid); const session = yield select(state => state.app.session_valid);
@ -105,14 +105,10 @@ export default {
}, },
*login({ payload }, { call, put, select }) { *login({ payload }, { call, put, select }) {
if (!payload) return false; if (!payload) return false;
const serverKey = yield select(state => state.app.server_key);
const requestPayload = { username: payload.username, password: payload.password, server_key: serverKey } const { user_id, access_token } = payload.authFrame
session.auth(requestPayload, (err, res) => {
if (err) { return yield put({ type: 'handleLogin', payload: { user_id, access_token, user_data: payload.dataFrame } })
const { status, message } = err;
return console.log(status, message);
}
});
}, },
*updateTheme({payload}, {put, select}){ *updateTheme({payload}, {put, select}){
if (!payload) return false if (!payload) return false
@ -146,6 +142,40 @@ export default {
...payload, ...payload,
}; };
}, },
updateFrames(state) {
let sessionAuthframe = sessionStorage.getItem('session')
let sessionDataframe = sessionStorage.getItem('data')
try {
if (sessionAuthframe) {
sessionAuthframe = JSON.parse(atob(sessionAuthframe))
}
if (sessionDataframe) {
sessionDataframe = JSON.parse(atob(sessionDataframe))
}
state.session_token = sessionAuthframe.session_token,
state.session_uuid = sessionAuthframe.session_uuid
state.session_data = sessionDataframe
} catch (error) {
verbosity.error(error)
}
},
handleLogin(state, { payload }){
if (!payload) return false
state.session_token = payload.access_token
state.session_uuid = payload.user_id
state.session_data = payload.user_data
const sessionAuthframe = btoa(JSON.stringify({session_token: payload.access_token, session_uuid: payload.user_id}))
const sessionDataframe = btoa(payload.user_data)
sessionStorage.setItem('session', sessionAuthframe)
sessionStorage.setItem('data', sessionDataframe)
notify.success('Login done!')
router.push('/')
},
handleThemeChange(state, { payload }) { handleThemeChange(state, { payload }) {
store.set('theme', payload); store.set('theme', payload);

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { request } from '@ragestudio/ycorejs-lib'; import { request } from 'api';
import { import {
Row, Row,
Col, Col,

View File

@ -0,0 +1,9 @@
import React from 'react'
import { PostCreator } from 'components'
export default class Explore_Page extends React.Component{
render(){
return(
<PostCreator />
)
}
}

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { connect } from 'umi'; import { connect } from 'umi';
import * as antd from 'antd' import * as antd from 'antd'
import * as Icons from 'components/Icons'
@connect(({ app }) => ({ app })) @connect(({ app }) => ({ app }))
class PageIndex extends React.PureComponent { class PageIndex extends React.PureComponent {
@ -37,7 +38,13 @@ class PageIndex extends React.PureComponent {
} }
const map = tmp.map(e => { const map = tmp.map(e => {
return(<div style={{ margin: '20px 50px 20px 10px' }} key={e.key} > <h4>{e.key}</h4><span>{JSON.stringify(e.value)}</span> </div>) const v = JSON.stringify(e.value)
return(
<div style={{ margin: '20px 50px 20px 10px' }} key={e.key} >
<h4>{e.key}</h4>
{v.length < 500? <span>{v}</span> : <antd.Collapse ><antd.Collapse.Panel header={`Hidden text ( ${v.length} Characters )`}><span>{v}</span></antd.Collapse.Panel></antd.Collapse>}
</div>
)
}) })
return map return map
@ -45,7 +52,7 @@ class PageIndex extends React.PureComponent {
return ( return (
<div> <div>
<antd.Card theme={window.DarkMode? "dark" : null} title="APP STATE"> <antd.Card style={{ wordBreak: 'break-all' }} title={<><Icons.Redux style={{ height: '19px', marginRight: '7px' }} /> Redux state</>}>
{AppState()} {AppState()}
</antd.Card> </antd.Card>

132
src/pages/login/index.js Normal file
View File

@ -0,0 +1,132 @@
import React from 'react'
import { app_info } from 'core'
import styles from './index.less'
import classnames from 'classnames'
import * as antd from 'antd'
import * as Icons from 'components/Icons'
import { RegistrationForm } from './register.js'
import { NormalLoginForm } from './login.js'
import { app_config } from 'config'
export function transitionToogle() {
window.LoginComponent.setState({
transition: !window.LoginComponent.state.transition,
})
window.LoginComponent.toogleYulioID()
}
class Login extends React.PureComponent {
constructor(props) {
super(props)
window.LoginComponent = this
this.state = {
transition: false,
using: 1,
}
}
switchType = {
f: a => {
this.setState({ using: a })
},
login: () => {
this.switchType.f(1)
},
register: () => {
this.switchType.f(2)
},
forgot: () => {
this.switchType.f(3)
},
}
renderType(t) {
const a = this.state.using
if (t) {
switch (a) {
case 1:
return `Sign in ${app_config.siteName}`
case 2:
return 'Register'
case 3:
return 'Forgot'
default:
return 'Auth'
}
} else {
switch (a) {
case 1:
return <NormalLoginForm />
case 2:
return <RegistrationForm />
case 3:
return null
default:
return <NormalLoginForm />
}
}
}
renderHelperButtons = () => {
if (this.state.using == 1) {
return (
<div className={styles.login_helper_footer}>
<antd.Button type="link" onClick={() => this.switchType.forgot()}>
Forgotten password
</antd.Button>
<antd.Button type="link" onClick={() => this.switchType.register()}>
Create an account
</antd.Button>
</div>
)
}
if (this.state.using == 2 || 3) {
return (
<div className={styles.login_helper_footer}>
<antd.Button type="link" onClick={() => this.switchType.login()}>
Login
</antd.Button>
</div>
)
}
}
render() {
return (
<div
className={classnames(styles.login_wrapper, {
[styles.goOut]: this.state.transition,
})}
>
<div
style={{
fontSize: '8px',
position: 'absolute',
top: '12px',
left: '12px',
}}
>
Using v{app_info.version} {app_info.stage}
</div>
<div className={styles.login_wrapper}>
<div className={styles.auth_box}>
<div className={styles.left_body}>
<h6>
<img className={styles.yid_logo} src={'https://api.ragestudio.net/id.svg'} /> YulioID&trade;
</h6>
<h2> {this.renderType(true)} </h2>
</div>
<div className={styles.right_body}>
{this.renderType()}
{this.renderHelperButtons()}
</div>
</div>
</div>
</div>
)
}
}
export default Login

234
src/pages/login/index.less Normal file
View File

@ -0,0 +1,234 @@
@import '~theme/index.less';
.login_wrapper{
width: 100%;
height: 100%;
font-family: "Poppins", sans-serif!important;
h1,h2,h3,h4,h5,h6{color: #333;}
position: absolute;
top: 0;
left: 0;
display: flex;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
margin: auto;
overflow-y: scroll;
overflow-x: hidden;
&.goOut{
.auth_box{
-webkit-animation-name: fadeOutLeft;
animation-name: fadeOutLeft;
}
}
transition: all 300ms ease-in-out;
}
.auth_box{
display: flex;
transition: all 300ms ease-in-out;
.yid_logo {
vertical-align: middle;
height: 17px;
}
:global{
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
border: 0;
-webkit-text-fill-color: #333;
-webkit-box-shadow: 0 0 0px 1000px #ffffff98 inset;
box-shadow: #fff 0px 0px 0px 1000px inset;
transition: background-color 5000s ease-in-out 0s;
}
.ant-input-affix-wrapper {
width: 100%;
height: 40px;
padding: 4px 11px;
color: #333;
font-size: 14px;
line-height: 1.5715;
background-color: #fff; //rgba(255, 255, 255, 0.596);
border: 1.5px #2F66DF solid;
border-radius: 7px;
}
}
}
.centering_wrapper{
width: 100%;
text-align: center;
}
.left_body{
z-index: 50;
transform: translate(12px,0);
float: left;
width: 30%;
color: #333;
background-color: #fff;
padding: 20px;
border-radius: 12px 0 0 12px;
box-shadow: 0px 10px 20px 0px rgba(51,51,51,0.52);
transition: all 300ms ease-in-out;
}
.right_body{
z-index: 51;
float: right;
width: 70%;
max-height: -webkit-fill-available;
height: 150px;
padding: 20px 50px 20px 50px;
color: #333;
background-color: #fff;
border-radius: 12px;
box-shadow: 0px 10px 20px 0px rgba(51,51,51,0.52);
transition: all 300ms ease-in-out;
}
.helper_login_btn{
transform: translate(-20px, -14px);
}
.login_helper_footer{
width: 100%;
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
margin: auto;
:global{
.ant-btn{
margin: auto;
padding: 0 5px 0 5px;
}
}
transform: translate(0, -10px);
}
@keyframes go-out {
0% {
filter: blur(0px)
}
100% {
filter: blur(15px)
}
}
// // Full format
// @media (min-width: 486px){
// .login_wrapper {
// min-height: 580px;
// }
// .auth_box {
// width: 784px;
// }
// }
// Medium format max-width: 830px
@media (min-width: 486px){
.auth_box {
padding: 0 16px 40px;
width: 500px;
min-height: 500px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.left_body{
width: 100%;
float: none;
border-radius: 12px 12px 0 0;
padding: 20px 20px 20px 20px;
transform: translate(0, 22px);
}
.right_body{
width: 100%;
float: none;
padding: 20px 60px 20px 60px;
}
}
// Mobile format
@media (max-width: 485px){
.auth_box {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 70px 16px 40px;
width: 100%;
height: 100%;
min-height: 500px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 0;
}
.left_body{
box-shadow: none;
width: 100%;
float: none;
position: absolute;
top: 0;
margin-top: 30px;
transform: translate(0, 0);
}
.right_body{
box-shadow: none;
width: 100%;
float: none;
padding: 20px 20px 20px 20px;
}
}
.register_form{
}
@-webkit-keyframes fadeOutLeft {
from {
opacity: 1;
}
to {
opacity: 0;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
}
@keyframes fadeOutLeft {
from {
opacity: 1;
}
to {
opacity: 0;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
}

277
src/pages/login/login.js Normal file
View File

@ -0,0 +1,277 @@
import React from 'react'
import styles from './index.less'
import Fade from 'react-reveal/Fade'
import HeadShake from 'react-reveal/HeadShake';
import * as antd from 'antd'
import { session, user } from 'core/cores'
import verbosity from 'core/libs/verbosity'
import { Form, Input, Button, Checkbox } from 'antd'
import {
UserOutlined,
LockOutlined,
BulbOutlined,
SwapLeftOutlined
} from 'components/Icons'
import { connect } from 'umi'
@connect(({ app }) => ({ app }))
export class NormalLoginForm extends React.PureComponent {
state = {
activeForm: true,
step: 1,
validating: false,
error_count: 0,
step_error: false,
step_show: true,
swpass: false,
}
next = values => {
let a = this.state.step
const b = btoa(Object.values(values).toString())
switch (a) {
case 1:
const payload = { username: Object.values(values).toString() }
user.get.profileData(payload, (err, res) => {
if (err || !res) return false
try {
const res_data = JSON.parse(res)
if (res_data.api_status == 200) {
a++
this.anim_transition(300)
this.setState({
step_error: false,
early_data: res_data.data,
form_rawd_1: b,
step: a,
})
}
if (res_data.api_status == 400) {
this.anim_error()
}
} catch (error) {
return false
}
})
return true
case 2:
this.setState({ form_rawd_2: b, step: a })
this.auth()
return true
default:
return false
}
}
back() {
let a = this.state.step
if (a > 1) {
a--
this.anim_transition(150)
}
this.setState({ step: a })
}
anim_transition(duration) {
this.setState({ step_show: false })
setTimeout(() => {
this.setState({ step_show: true })
}, duration || 1000)
}
anim_error() {
this.setState({ step_error: true, error_count: (this.state.error_count + 1) })
}
anim_close() {
this.setState({ step_show: false })
}
getAuthFrame(payload) {
return new Promise(resolve => {
session.auth(payload, (err, res) => {
if (err) {
}
if (res) {
try {
res = JSON.parse(res)
verbosity.log(res)
} catch (error) {
console.log('Invalid response!')
}
switch (res.api_status.toString()) {
case "200": {
try {
return resolve(res)
} catch (error) {
verbosity.error(error)
}
break;
}
case "400": {
console.log('Credentials error')
this.setState({ validating: false })
return this.anim_error()
}
case "500": {
console.log('Server error')
this.setState({ validating: false })
return this.back()
}
default: {
console.log('Unknown error')
this.setState({ validating: false })
return this.back()
}
}
}
})
});
}
getDataFrame(payload) {
return new Promise(resolve => {
user.get.data(payload, (err, res) => {
if(err) {
}
if (res) {
try {
return resolve(JSON.stringify(JSON.parse(res)['user_data']))
} catch (error) {
verbosity.error(error)
}
}
})
})
}
async auth() {
const { form_rawd_1, form_rawd_2 } = this.state
if (!form_rawd_1 || !form_rawd_2) return false
this.setState({ step_error: false, validating: true })
const authFrame = await this.getAuthFrame({username: form_rawd_1, password: form_rawd_2, server_key: this.props.app.server_key})
const dataFrame = await this.getDataFrame({id: authFrame.user_id, access_token: authFrame.access_token, serverKey: this.props.app.server_key})
return this.props.dispatch({
type: 'app/login',
payload: {authFrame, dataFrame}
});
}
renderState = () => {
switch (this.state.step) {
case 1:
return (
<Form
name="signin_username"
className="login-form"
onFinish={this.next}
>
<h5>
<BulbOutlined /> You can use your YulioID account to login
</h5>
<HeadShake spy={this.state.error_count}>
<Form.Item
name="username"
hasFeedback
help={this.state.step_error? "It seems that this user does not exist" : null}
validateStatus={this.state.step_error? 'error' : this.state.validating? 'validating' : null}
rules={[
{
required: true,
message: 'Please use your Username or Email!',
},
]}
>
<Input
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Username or Email"
/>
</Form.Item>
</HeadShake>
<Button
type="primary"
htmlType="submit"
className="login-form-button"
>
Next
</Button>
</Form>
)
case 2:
return (
<Form
name="signin_password"
className="login-form"
onFinish={this.next}
>
<h4><antd.Avatar shape='square' src={this.state.early_data.avatar} /> Welcome Back @{this.state.early_data.username}</h4>
<HeadShake spy={this.state.error_count}>
<Form.Item
name="password"
hasFeedback
help={this.state.step_error? "Incorrect password" : null}
validateStatus={this.state.step_error? 'error' : this.state.validating? 'validating' : null}
rules={[
{ required: true, message: 'Please input your Password!' },
]}
>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon" />}
type={this.state.swpass ? 'text' : 'password'}
placeholder="Password"
/>
</Form.Item>
</HeadShake>
<div className={styles.helper_login_btn}>
<antd.Button
icon={<SwapLeftOutlined />}
type="link"
onClick={() => this.back()}
>
Back
</antd.Button>
<Button
type="primary"
htmlType="submit"
className="login-form-button"
>
Login
</Button>
</div>
</Form>
)
case 3: {
return <h3>Wait a sec...</h3>
}
default:
return null
}
}
render() {
return (
<div className={styles.login_form}>
<Fade left opposite when={this.state.step_show}>
{this.state.activeForm? this.renderState() : <div><h4>Mmm, this is taking longer than it should...</h4></div>}
</Fade>
</div>
)
}
}

162
src/pages/login/register.js Normal file
View File

@ -0,0 +1,162 @@
import React, { useState } from 'react'
import {
MailOutlined,
TagOutlined,
LockOutlined,
} from '@ant-design/icons'
import styles from './index.less'
import {
Form,
Input,
Tooltip,
Cascader,
Select,
Row,
Col,
Checkbox,
Button,
AutoComplete,
} from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import ReCAPTCHA from 'react-google-recaptcha'
import { g_recaptcha_key } from 'config'
function capchaOnChange(value) {
console.log('Captcha value:', value)
}
export const RegistrationForm = () => {
const onFinish = values => {
console.log('Received values of form: ', values)
}
return (
<div className={styles.centering_wrapper}>
<Form
name="register"
className={styles.register_form}
onFinish={onFinish}
scrollToFirstError
>
<Form.Item
name="username"
rules={[
{
required: true,
message: 'Please input your username!',
whitespace: true,
},
]}
>
<Input prefix={<TagOutlined />}
placeholder="ramdomuser"/>
</Form.Item>
<Form.Item
name="email"
rules={[
{
type: 'email',
message: 'The input is not valid E-mail!',
},
{
required: true,
message: 'Please input your E-mail!',
},
]}
>
<Input
prefix={<MailOutlined />}
placeholder="example@no-real.com"
/>
</Form.Item>
<Form.Item
name="password"
rules={[
{
required: true,
message: 'Please input your password!',
},
]}
hasFeedback
>
<Input.Password prefix={<LockOutlined />}
placeholder="example@no-real.com"/>
</Form.Item>
<Form.Item
name="confirm"
dependencies={['password']}
hasFeedback
rules={[
{
required: true,
message: 'Please confirm your password!',
},
({ getFieldValue }) => ({
validator(rule, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve()
}
return Promise.reject(
'The two passwords that you entered do not match!'
)
},
}),
]}
>
<Input.Password prefix={<LockOutlined />}
placeholder="example@no-real.com"/>
</Form.Item>
<Form.Item extra="We must make sure that your are a human.">
<Row gutter={8}>
<Col span={12}>
<Form.Item
name="captcha"
noStyle
rules={[
{
required: true,
message: 'Please complete the captcha!',
},
]}
>
<ReCAPTCHA sitekey={g_recaptcha_key} onChange={capchaOnChange} />
</Form.Item>
</Col>
<Col span={12}></Col>
</Row>
</Form.Item>
<Form.Item
name="agreement"
valuePropName="checked"
rules={[
{
validator: (_, value) =>
value
? Promise.resolve()
: Promise.reject('Should accept agreement'),
},
]}
>
<Checkbox>
I have read the <a href="">agreement</a>
</Checkbox>
</Form.Item>
<Form.Item >
<Button type="primary" htmlType="submit">
Register
</Button>
</Form.Item>
</Form>
</div>
)
}