mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
update: app model, login & authframes
This commit is contained in:
parent
90bbda47b2
commit
fc5e4a0106
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,4 +13,4 @@
|
||||
/src/.umi-production
|
||||
/src/.umi-test
|
||||
/.env.local
|
||||
api/
|
||||
|
||||
|
20
.umirc.js
20
.umirc.js
@ -1,25 +1,7 @@
|
||||
import { defineConfig } from 'umi';
|
||||
|
||||
const Path = 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({
|
||||
hash: false,
|
||||
ignoreMomentLocale: true,
|
||||
@ -33,7 +15,7 @@ export default defineConfig({
|
||||
},
|
||||
alias: {
|
||||
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'),
|
||||
core: resolve(__dirname, './src/core'),
|
||||
theme: resolve(__dirname, './src/theme'),
|
||||
|
@ -4,4 +4,5 @@ export default {
|
||||
logout: 'POST /logout',
|
||||
|
||||
get_data: 'POST /get-user-data',
|
||||
profileData: 'POST /early_user'
|
||||
};
|
||||
|
@ -50,7 +50,9 @@ module.exports = {
|
||||
render_pagetransition_preset: 'moveToRightScaleUp',
|
||||
|
||||
feed_autorefresh: false,
|
||||
},
|
||||
|
||||
stricts: {
|
||||
post_maxlenght: '512',
|
||||
post_catchlimit: '20',
|
||||
post_hidebar: true,
|
||||
|
@ -3,7 +3,7 @@
|
||||
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
|
||||
"title": "Comty™",
|
||||
"DevBuild": true,
|
||||
"version": "0.8.08",
|
||||
"version": "0.8.22",
|
||||
"stage": "dev-pre",
|
||||
"description": "",
|
||||
"author": "RageStudio",
|
||||
@ -36,6 +36,7 @@
|
||||
"@material-ui/core": "^4.9.9",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@ragestudio/ycorejs-lib": "^0.1.22",
|
||||
"@steveeeie/react-page-transition": "^1.2.0",
|
||||
"antd": "^4.5.1",
|
||||
"axios": "^0.19.2",
|
||||
"babel-core": "^6.26.3",
|
||||
|
@ -4,7 +4,7 @@ import styles from './Loader.less'
|
||||
|
||||
const Loader = (loading) => {
|
||||
return (
|
||||
<div className={classNames(styles.wrapper, {[styles.end]: !loading.spinning })}>
|
||||
<div className={classNames(styles.wrapper, {[styles.end]: !loading })}>
|
||||
<span>Loading... </span>
|
||||
<div
|
||||
className={styles.newloader}
|
||||
|
322
src/components/PostCreator/index.js
Normal file
322
src/components/PostCreator/index.js
Normal 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
|
315
src/components/PostCreator/index.less
Normal file
315
src/components/PostCreator/index.less
Normal 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;
|
||||
}
|
@ -15,6 +15,7 @@ import UserBadges from './UserBadges'
|
||||
|
||||
// Post Components
|
||||
import MediaPlayer from './MediaPlayer'
|
||||
import PostCreator from './PostCreator'
|
||||
|
||||
// Mix & Export all
|
||||
export {
|
||||
@ -25,5 +26,6 @@ export {
|
||||
UserBadges,
|
||||
PageTransition,
|
||||
MyLayout,
|
||||
Loader
|
||||
Loader,
|
||||
PostCreator
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
export * from './session';
|
||||
export * from './user';
|
||||
export * from './router'
|
||||
|
||||
import * as user from './user'
|
||||
import * as session from './session'
|
||||
|
||||
export { user, session }
|
@ -11,18 +11,18 @@ import { history } from 'umi';
|
||||
* Specify the paths of the files, in this case it is pointing to the root
|
||||
*/
|
||||
export const router = {
|
||||
go: e => {
|
||||
goTo.element('primaryContent');
|
||||
router.push(e);
|
||||
},
|
||||
push: e => {
|
||||
history.push({
|
||||
pathname: `${e}`,
|
||||
});
|
||||
},
|
||||
goprofile: () => {
|
||||
go: e => {
|
||||
router.push(e);
|
||||
goTo.element('primaryContent');
|
||||
},
|
||||
goprofile: () => {
|
||||
router.push(`/@${e}`);
|
||||
goTo.element('primaryContent');
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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 { v3_model } from 'core/libs';
|
||||
|
||||
|
||||
// @param {Array} payload callback - check the information and if it is correct give access
|
||||
function auth(payload, callback) {
|
||||
if (!payload) return false;
|
||||
const { username, password, server_key } = payload;
|
||||
|
||||
if (username && password) {
|
||||
const frame = { username: atob(username), password: atob(password) }
|
||||
v3_model.api_request(
|
||||
{
|
||||
body: frame,
|
||||
endpoint: endpoints.auth,
|
||||
serverKey: server_key,
|
||||
verbose: true,
|
||||
},
|
||||
(err, res) => {
|
||||
console.log(err, res);
|
||||
return callback(err, res);
|
||||
},
|
||||
);
|
||||
return callback(false, true);
|
||||
} else {
|
||||
const res = { status: 100, message: 'Invalid Credentials!' };
|
||||
return callback(res, false);
|
||||
}
|
||||
}
|
||||
|
||||
async function deauth() {}
|
||||
function deauth() {
|
||||
|
||||
}
|
||||
|
||||
// check the information and if it is correct give access
|
||||
const backup = {
|
||||
get: () => {},
|
||||
set: () => {},
|
||||
};
|
||||
|
||||
|
||||
|
||||
export { auth, deauth, backup };
|
||||
|
@ -7,21 +7,28 @@ const set = {
|
||||
},
|
||||
};
|
||||
|
||||
const get = {
|
||||
data: parms => {
|
||||
export const get = {
|
||||
data: (parms, callback) => {
|
||||
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
|
||||
}
|
||||
v3_model.api_request(
|
||||
{
|
||||
body: {user_id: id, fetch: req.fetch},
|
||||
serverKey: serverKey,
|
||||
userToken: access_token,
|
||||
endpoint: endpoints.get_data,
|
||||
verbose: true,
|
||||
},
|
||||
(err, res) => {
|
||||
console.log(err, res);
|
||||
return callback(err, res)
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -33,17 +40,32 @@ const get = {
|
||||
// core get id data from current session
|
||||
}
|
||||
},
|
||||
profileData: parms => {
|
||||
if (!parms) return false;
|
||||
const { id } = parms;
|
||||
profileData: (parms, callback) => {
|
||||
if (!parms) return false
|
||||
|
||||
if (!id) {
|
||||
// core get id data from current session
|
||||
const { username } = parms
|
||||
|
||||
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 => {
|
||||
if (!parms) return false;
|
||||
const { id, toID } = parms;
|
||||
@ -58,8 +80,3 @@ const actions = {
|
||||
},
|
||||
};
|
||||
|
||||
export {
|
||||
//set
|
||||
get,
|
||||
actions,
|
||||
};
|
||||
|
@ -1,13 +1,10 @@
|
||||
import { lib, v3_request } from 'api';
|
||||
import endpoints_list from 'config/endpoints';
|
||||
import { app_config } from 'config';
|
||||
import * as core from 'core'
|
||||
import { connect } from 'dva';
|
||||
|
||||
const { api_prefix } = app_config;
|
||||
const { uri_resolver } = lib;
|
||||
|
||||
|
||||
async function compileURI(e, callback) {
|
||||
const resolvers = await uri_resolver();
|
||||
const prefix = resolvers[api_prefix];
|
||||
@ -15,7 +12,6 @@ async function compileURI(e, callback) {
|
||||
let final = null;
|
||||
let url;
|
||||
let method;
|
||||
|
||||
const endpointSplit = e.split(' ');
|
||||
if (endpointSplit.length === 2) {
|
||||
method = endpointSplit[0];
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { PureComponent, Fragment } from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { Loader } from 'components'
|
||||
@ -8,39 +8,44 @@ import { queryLayout } from 'core'
|
||||
import config from 'config'
|
||||
|
||||
import PrimaryLayout from './PrimaryLayout'
|
||||
import PublicLayout from './PublicLayout'
|
||||
import './BaseLayout.less'
|
||||
|
||||
const LayoutMap = {
|
||||
primary: PrimaryLayout
|
||||
// public: PublicLayout,
|
||||
primary: PrimaryLayout,
|
||||
public: PublicLayout,
|
||||
}
|
||||
|
||||
@withRouter
|
||||
@connect(({ app, loading }) => ({ app, loading }))
|
||||
class BaseLayout extends PureComponent {
|
||||
class BaseLayout extends React.Component {
|
||||
previousPath = ''
|
||||
renderLoading = true
|
||||
|
||||
render() {
|
||||
const { loading, children, location } = this.props
|
||||
const Container = LayoutMap[queryLayout(config.layouts, location.pathname)]
|
||||
|
||||
const currentPath = location.pathname + location.search
|
||||
|
||||
if (currentPath !== this.previousPath) {
|
||||
NProgress.start()
|
||||
this.renderLoading = true
|
||||
}
|
||||
|
||||
if (!loading.global) {
|
||||
NProgress.done()
|
||||
this.previousPath = currentPath
|
||||
this.renderLoading = false
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<React.Fragment>
|
||||
<Helmet>
|
||||
<title>{config.app_config.siteName}</title>
|
||||
</Helmet>
|
||||
{Loader(loading)}
|
||||
{Loader(this.renderLoading)}
|
||||
<Container>{children}</Container>
|
||||
</Fragment>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import PropTypes from 'prop-types'
|
||||
import {withRouter, connect} from 'umi'
|
||||
import {
|
||||
MyLayout,
|
||||
PageTransition,
|
||||
} from 'components'
|
||||
import { enquireScreen, unenquireScreen } from 'enquire-js'
|
||||
import store from 'store'
|
||||
@ -103,17 +102,13 @@ class PrimaryLayout extends React.Component {
|
||||
<antd.Layout id="app" className={isActive(currentTheme['darkmode'])? "dark_mode" : null }>
|
||||
<Sider {...SiderProps} />
|
||||
<div className={styles.primary_layout_container}>
|
||||
<PageTransition
|
||||
preset="moveToRightScaleUp"
|
||||
transitionKey={location.pathname}
|
||||
>
|
||||
|
||||
<Content
|
||||
id="primaryContent"
|
||||
className={styles.primary_layout_content}
|
||||
>
|
||||
{children? children : null}
|
||||
</Content>
|
||||
</PageTransition>
|
||||
</div>
|
||||
<Overlay {...OverlayProps} />
|
||||
</antd.Layout>
|
||||
|
67
src/layouts/PublicLayout.js
Normal file
67
src/layouts/PublicLayout.js
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
@ -3,10 +3,9 @@ import store from 'store';
|
||||
import { pathMatchRegexp, queryLayout } from 'core';
|
||||
import { app_config } from 'config';
|
||||
import keys from 'config/app_keys';
|
||||
import * as core from 'core';
|
||||
import { session } from 'core/cores';
|
||||
import { router } from 'core/cores';
|
||||
import verbosity from 'core/libs/verbosity'
|
||||
import { theme } from 'core/libs/style'
|
||||
import { notify } from 'core/libs/interface/notify'
|
||||
|
||||
export default {
|
||||
namespace: 'app',
|
||||
@ -17,7 +16,7 @@ export default {
|
||||
ng_services: false,
|
||||
session_valid: false,
|
||||
|
||||
session_token: null,
|
||||
session_token: sessionStorage.getItem('session'),
|
||||
session_data: null,
|
||||
session_uuid: null,
|
||||
|
||||
@ -63,30 +62,31 @@ export default {
|
||||
effects: {
|
||||
*query({ payload }, { call, put, select }) {
|
||||
const service = yield select(state => state.app.service_valid);
|
||||
const session = yield select(state => state.app.session_valid);
|
||||
|
||||
yield put({ type: 'updateFrames' })
|
||||
|
||||
if (!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;
|
||||
} else if (
|
||||
!pathMatchRegexp(['', '/login'], window.location.pathname) &&
|
||||
queryLayout(config.layouts, window.location.pathname) !== 'public'
|
||||
) {
|
||||
if (validBackup == true) {
|
||||
// logout normal
|
||||
} else {
|
||||
core.router.push({ pathname: '/login' });
|
||||
}
|
||||
}
|
||||
// if (session) {
|
||||
// if (pathMatchRegexp(['/', '/login'], window.location.pathname)) {
|
||||
// app.router.push({ pathname: `${app_config.MainPath}` });
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// } else if (
|
||||
// !pathMatchRegexp(['', '/login'], window.location.pathname) &&
|
||||
// queryLayout(config.layouts, window.location.pathname) !== 'public'
|
||||
// ) {
|
||||
// if (validBackup == true) {
|
||||
// // logout normal
|
||||
// } else {
|
||||
// core.router.push({ pathname: '/login' });
|
||||
// }
|
||||
// }
|
||||
},
|
||||
*update({ payload }, { call, put, select }) {
|
||||
const session = yield select(state => state.app.session_valid);
|
||||
@ -105,14 +105,10 @@ export default {
|
||||
},
|
||||
*login({ payload }, { call, put, select }) {
|
||||
if (!payload) return false;
|
||||
const serverKey = yield select(state => state.app.server_key);
|
||||
const requestPayload = { username: payload.username, password: payload.password, server_key: serverKey }
|
||||
session.auth(requestPayload, (err, res) => {
|
||||
if (err) {
|
||||
const { status, message } = err;
|
||||
return console.log(status, message);
|
||||
}
|
||||
});
|
||||
|
||||
const { user_id, access_token } = payload.authFrame
|
||||
|
||||
return yield put({ type: 'handleLogin', payload: { user_id, access_token, user_data: payload.dataFrame } })
|
||||
},
|
||||
*updateTheme({payload}, {put, select}){
|
||||
if (!payload) return false
|
||||
@ -146,6 +142,40 @@ export default {
|
||||
...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 }) {
|
||||
store.set('theme', payload);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { request } from '@ragestudio/ycorejs-lib';
|
||||
import { request } from 'api';
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
|
9
src/pages/explore/index.js
Normal file
9
src/pages/explore/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
import { PostCreator } from 'components'
|
||||
export default class Explore_Page extends React.Component{
|
||||
render(){
|
||||
return(
|
||||
<PostCreator />
|
||||
)
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'umi';
|
||||
import * as antd from 'antd'
|
||||
import * as Icons from 'components/Icons'
|
||||
|
||||
@connect(({ app }) => ({ app }))
|
||||
class PageIndex extends React.PureComponent {
|
||||
@ -37,7 +38,13 @@ class PageIndex extends React.PureComponent {
|
||||
}
|
||||
|
||||
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
|
||||
@ -45,7 +52,7 @@ class PageIndex extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<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()}
|
||||
</antd.Card>
|
||||
|
||||
|
132
src/pages/login/index.js
Normal file
132
src/pages/login/index.js
Normal 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™
|
||||
</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
234
src/pages/login/index.less
Normal 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
277
src/pages/login/login.js
Normal 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
162
src/pages/login/register.js
Normal 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>
|
||||
)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user