mirror of
https://github.com/status-im/discover-dapps.git
synced 2025-02-20 14:18:28 +00:00
create dapp
This commit is contained in:
parent
fb58dee434
commit
fe4f2dd055
@ -26,6 +26,8 @@
|
||||
"redux": "^4.0.1",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"reselect": "^4.0.0",
|
||||
"rc-slider": "8.6.9",
|
||||
"rc-tooltip": "3.7.3",
|
||||
"web3-utils": "^1.0.0-beta.35"
|
||||
},
|
||||
"scripts": {
|
||||
|
57
src/common/assets/images/loading-spinner.svg
Normal file
57
src/common/assets/images/loading-spinner.svg
Normal file
@ -0,0 +1,57 @@
|
||||
<svg class="lds-spin" width="16px" height="16px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><g transform="translate(80,50)">
|
||||
<g transform="rotate(0)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="1">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.875s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.875s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,71.21320343559643)">
|
||||
<g transform="rotate(45)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.875">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.75s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.75s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(50,80)">
|
||||
<g transform="rotate(90)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.75">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.625s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.625s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.786796564403577,71.21320343559643)">
|
||||
<g transform="rotate(135)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.625">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.5s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.5s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(20,50.00000000000001)">
|
||||
<g transform="rotate(180)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.5">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.375s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.375s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.78679656440357,28.786796564403577)">
|
||||
<g transform="rotate(225)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.375">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.25s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.25s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(49.99999999999999,20)">
|
||||
<g transform="rotate(270)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.25">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.125s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.125s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,28.78679656440357)">
|
||||
<g transform="rotate(315)">
|
||||
<circle cx="0" cy="0" r="10" fill="#4360df" fill-opacity="0.125">
|
||||
<animateTransform attributeName="transform" type="scale" begin="0s" values="1.1 1.1;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="0s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g></svg>
|
After Width: | Height: | Size: 3.7 KiB |
24
src/common/components/Slider/Slider.jsx
Normal file
24
src/common/components/Slider/Slider.jsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import RCSlider from 'rc-slider'
|
||||
|
||||
import './Slider.scss'
|
||||
|
||||
const Slider = props => {
|
||||
const { min, max, value, onChange } = props
|
||||
|
||||
return (
|
||||
<div className="slider">
|
||||
<RCSlider min={min} max={max} value={value} onChange={onChange} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Slider.propTypes = {
|
||||
min: PropTypes.number.isRequired,
|
||||
max: PropTypes.number.isRequired,
|
||||
value: PropTypes.number.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Slider
|
28
src/common/components/Slider/Slider.scss
Normal file
28
src/common/components/Slider/Slider.scss
Normal file
@ -0,0 +1,28 @@
|
||||
@import '../../../common/styles/variables';
|
||||
|
||||
.slider {
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.slider .rc-slider-rail {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.slider .rc-slider-track {
|
||||
height: 8px;
|
||||
background-color: $link-color;
|
||||
}
|
||||
|
||||
.slider .rc-slider-handle {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-top: -9px;
|
||||
margin-left: -13px;
|
||||
border: 0;
|
||||
background: $link-color;
|
||||
}
|
||||
|
||||
.slider .rc-slider-handle:hover,
|
||||
.slider .rc-slider-handle:active {
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.48);
|
||||
}
|
3
src/common/components/Slider/index.js
Normal file
3
src/common/components/Slider/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import Slider from './Slider'
|
||||
|
||||
export default Slider
|
@ -5,6 +5,11 @@ const submit = {
|
||||
url: '',
|
||||
img: '',
|
||||
category: '',
|
||||
imgControl: false,
|
||||
imgControlZoom: 0,
|
||||
imgControlMove: false,
|
||||
imgControlX: 0,
|
||||
imgControlY: 0,
|
||||
}
|
||||
|
||||
export default submit
|
||||
|
8
src/common/data/transaction-status.js
Normal file
8
src/common/data/transaction-status.js
Normal file
@ -0,0 +1,8 @@
|
||||
const transactionStatus = {
|
||||
dappName: 'slow.Trade',
|
||||
dappUrl: '/images/dapps/slowtrade.png',
|
||||
progress: false,
|
||||
published: true,
|
||||
}
|
||||
|
||||
export default transactionStatus
|
@ -5,6 +5,7 @@ import dapps from '../../modules/Dapps/Dapps.reducer'
|
||||
import vote from '../../modules/Vote/Vote.reducer'
|
||||
import submit from '../../modules/Submit/Submit.reducer'
|
||||
import desktopMenu from '../../modules/DesktopMenu/DesktopMenu.reducer'
|
||||
import transactionStatus from '../../modules/TransactionStatus/TransactionStatus.recuder'
|
||||
|
||||
export default history =>
|
||||
combineReducers({
|
||||
@ -14,4 +15,5 @@ export default history =>
|
||||
vote,
|
||||
submit,
|
||||
desktopMenu,
|
||||
transactionStatus,
|
||||
})
|
||||
|
@ -8,6 +8,7 @@ import Dapps from '../Dapps'
|
||||
import Vote from '../Vote'
|
||||
import Submit from '../Submit'
|
||||
import Terms from '../Terms/Terms'
|
||||
import TransactionStatus from '../TransactionStatus'
|
||||
|
||||
class Router extends React.Component {
|
||||
componentDidMount() {
|
||||
@ -27,6 +28,7 @@ class Router extends React.Component {
|
||||
</Switch>,
|
||||
<Vote key={2} />,
|
||||
<Submit key={3} />,
|
||||
<TransactionStatus key={4} />,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,12 @@ import {
|
||||
onInputNameAction,
|
||||
onInputUrlAction,
|
||||
onInputDescAction,
|
||||
onImgReadAction,
|
||||
onImgZoomAction,
|
||||
onImgMoveControlAction,
|
||||
onImgMoveAction,
|
||||
onImgDoneAction,
|
||||
onImgCancelAction,
|
||||
} from './Submit.reducer'
|
||||
|
||||
const mapStateToProps = state => state.submit
|
||||
@ -15,6 +21,12 @@ const mapDispatchToProps = dispatch => ({
|
||||
onInputName: name => dispatch(onInputNameAction(name)),
|
||||
onInputDesc: name => dispatch(onInputDescAction(name)),
|
||||
onInputUrl: name => dispatch(onInputUrlAction(name)),
|
||||
onImgRead: imgBase64 => dispatch(onImgReadAction(imgBase64)),
|
||||
onImgZoom: zoom => dispatch(onImgZoomAction(zoom)),
|
||||
onImgMoveControl: move => dispatch(onImgMoveControlAction(move)),
|
||||
onImgMove: (x, y) => dispatch(onImgMoveAction(x, y)),
|
||||
onImgCancel: () => dispatch(onImgCancelAction()),
|
||||
onImgDone: imgBase64 => dispatch(onImgDoneAction(imgBase64)),
|
||||
})
|
||||
|
||||
export default withRouter(
|
||||
|
@ -3,17 +3,59 @@ import PropTypes from 'prop-types'
|
||||
import styles from './Submit.module.scss'
|
||||
import Modal from '../../common/components/Modal'
|
||||
import CategorySelector from '../CategorySelector/CategorySelector.picker'
|
||||
import Slider from '../../common/components/Slider/Slider'
|
||||
import 'rc-slider/assets/index.css'
|
||||
import 'rc-tooltip/assets/bootstrap.css'
|
||||
|
||||
class Submit extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.imgCanvas = React.createRef()
|
||||
this.previousMoveX = 0
|
||||
this.previousMoveY = 0
|
||||
this.onInputName = this.onInputName.bind(this)
|
||||
this.onInputDesc = this.onInputDesc.bind(this)
|
||||
this.onInputUrl = this.onInputUrl.bind(this)
|
||||
this.onChangeImage = this.onChangeImage.bind(this)
|
||||
this.onChangeZoom = this.onChangeZoom.bind(this)
|
||||
this.onStartMove = this.onStartMove.bind(this)
|
||||
this.onMouseMove = this.onMouseMove.bind(this)
|
||||
this.onTouchMove = this.onTouchMove.bind(this)
|
||||
this.onEndMove = this.onEndMove.bind(this)
|
||||
this.onImgDone = this.onImgDone.bind(this)
|
||||
}
|
||||
|
||||
onSelectCategory(category) {
|
||||
this.setState({ category: category })
|
||||
componentDidUpdate() {
|
||||
const { img, imgControlZoom, imgControlX, imgControlY } = this.props
|
||||
if (img === '') return
|
||||
|
||||
const canvas = this.imgCanvas.current
|
||||
if (canvas === null) return
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
const imgNode = new Image()
|
||||
imgNode.onload = () => {
|
||||
const s = parseInt(
|
||||
Math.min(imgNode.width, imgNode.height) /
|
||||
((imgControlZoom + 100) / 100),
|
||||
10,
|
||||
)
|
||||
const k = canvas.width / s
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.drawImage(
|
||||
imgNode,
|
||||
0,
|
||||
0,
|
||||
imgNode.width,
|
||||
imgNode.height,
|
||||
imgControlX + (s - imgNode.width) * 0.5 * k,
|
||||
imgControlY + (s - imgNode.height) * 0.5 * k,
|
||||
imgNode.width * k,
|
||||
imgNode.height * k,
|
||||
)
|
||||
}
|
||||
imgNode.src = img
|
||||
}
|
||||
|
||||
onInputName(e) {
|
||||
@ -31,18 +73,98 @@ class Submit extends React.Component {
|
||||
onInputUrl(e.target.value)
|
||||
}
|
||||
|
||||
onChangeImage(e) {
|
||||
const input = e.target
|
||||
const { files } = e.target
|
||||
if (files === 0) return
|
||||
|
||||
const file = files[0]
|
||||
const fileReader = new FileReader()
|
||||
fileReader.onload = ev => {
|
||||
const { onImgRead } = this.props
|
||||
input.value = ''
|
||||
onImgRead(ev.target.result)
|
||||
}
|
||||
fileReader.readAsDataURL(file, 'UTF-8')
|
||||
}
|
||||
|
||||
onChangeZoom(value) {
|
||||
const { onImgZoom } = this.props
|
||||
onImgZoom(value)
|
||||
}
|
||||
|
||||
onStartMove() {
|
||||
const { onImgMoveControl } = this.props
|
||||
this.previousMoveX = -1
|
||||
this.previousMoveY = -1
|
||||
onImgMoveControl(true)
|
||||
}
|
||||
|
||||
onMouseMove(e) {
|
||||
const { imgControlMove, imgControlX, imgControlY, onImgMove } = this.props
|
||||
if (!imgControlMove) return
|
||||
const x = imgControlX + e.movementX
|
||||
const y = imgControlY + e.movementY
|
||||
requestAnimationFrame(() => {
|
||||
onImgMove(x, y)
|
||||
})
|
||||
}
|
||||
|
||||
onTouchMove(e) {
|
||||
const { imgControlMove, imgControlX, imgControlY, onImgMove } = this.props
|
||||
if (!imgControlMove) return
|
||||
|
||||
const touch = e.touches[0]
|
||||
if (this.previousMoveX !== -1 && this.previousMoveY !== -1) {
|
||||
const x = imgControlX - (this.previousMoveX - touch.screenX)
|
||||
const y = imgControlY - (this.previousMoveY - touch.screenY)
|
||||
requestAnimationFrame(() => {
|
||||
onImgMove(x, y)
|
||||
})
|
||||
}
|
||||
|
||||
this.previousMoveX = touch.screenX
|
||||
this.previousMoveY = touch.screenY
|
||||
}
|
||||
|
||||
onEndMove() {
|
||||
const { onImgMoveControl } = this.props
|
||||
onImgMoveControl(false)
|
||||
}
|
||||
|
||||
onImgDone() {
|
||||
const { onImgDone } = this.props
|
||||
const canvas = this.imgCanvas.current
|
||||
const imgBase64 = canvas.toDataURL('image/jpg')
|
||||
onImgDone(imgBase64)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { visible, onClickClose, name, desc, url, img, category } = this.props
|
||||
const {
|
||||
visible,
|
||||
onClickClose,
|
||||
name,
|
||||
desc,
|
||||
url,
|
||||
img,
|
||||
category,
|
||||
imgControl,
|
||||
imgControlZoom,
|
||||
onImgCancel,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible && window.location.hash === '#submit'}
|
||||
onClickClose={onClickClose}
|
||||
windowClassName={styles.modalWindow}
|
||||
contentClassName={imgControl ? styles.modalContentImgControl : ''}
|
||||
>
|
||||
<div className={styles.title}>Submit a Ðapp</div>
|
||||
<div className={styles.cnt}>
|
||||
<div>
|
||||
<div className={styles.title}>
|
||||
{imgControl ? 'Position and size your image' : 'Submit a Ðapp'}
|
||||
</div>
|
||||
<div className={imgControl ? styles.cntWithImgControl : ''}>
|
||||
<div className={imgControl ? styles.withImgControl : ''}>
|
||||
<div className={styles.block}>
|
||||
<div className={styles.labelRow}>
|
||||
<span>Name of your Ðapp</span>
|
||||
@ -85,7 +207,13 @@ class Submit extends React.Component {
|
||||
<span>Choose image</span>
|
||||
<div
|
||||
className={styles.imgHolder}
|
||||
style={{ backgroundImage: img }}
|
||||
style={{ backgroundImage: `url(${img})` }}
|
||||
/>
|
||||
<input
|
||||
className={styles.uploader}
|
||||
type="file"
|
||||
onChange={this.onChangeImage}
|
||||
accept=".jpg, .png"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.imgInfo}>
|
||||
@ -106,8 +234,8 @@ class Submit extends React.Component {
|
||||
</div>
|
||||
<div className={`${styles.block} ${styles.blockSubmit}`}>
|
||||
<div className={styles.terms}>
|
||||
By continuing you agree to our{' '}
|
||||
<a href="#">Terms and Conditions.</a>{' '}
|
||||
By continuing you agree to our
|
||||
<a href="#">Terms and Conditions.</a>
|
||||
</div>
|
||||
<button
|
||||
className={styles.submitButton}
|
||||
@ -118,7 +246,54 @@ class Submit extends React.Component {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.imgResizer} />
|
||||
{imgControl && (
|
||||
<div className={styles.imgControl}>
|
||||
<div
|
||||
className={styles.imgCanvasCnt}
|
||||
onMouseDown={this.onStartMove}
|
||||
onMouseMove={this.onMouseMove}
|
||||
onMouseUp={this.onEndMove}
|
||||
onMouseLeave={this.onEndMove}
|
||||
onTouchStart={this.onStartMove}
|
||||
onTouchMove={this.onTouchMove}
|
||||
onTouchEnd={this.onEndMove}
|
||||
onTouchCancel={this.onEndMove}
|
||||
>
|
||||
<canvas
|
||||
ref={this.imgCanvas}
|
||||
className={styles.imgCanvas}
|
||||
width="160"
|
||||
height="160"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.controls}>
|
||||
<div className={styles.slider}>
|
||||
<div className={styles.minZoom} />
|
||||
<Slider
|
||||
min={0}
|
||||
max={300}
|
||||
value={imgControlZoom}
|
||||
onChange={this.onChangeZoom}
|
||||
/>
|
||||
<div className={styles.maxZoom} />
|
||||
</div>
|
||||
<div className={styles.actionsCnt}>
|
||||
<button
|
||||
className={`${styles.button} ${styles.cancelButton}`}
|
||||
onClick={onImgCancel}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.button} ${styles.doneButton}`}
|
||||
onClick={this.onImgDone}
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
@ -132,10 +307,21 @@ Submit.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
img: PropTypes.string.isRequired,
|
||||
category: PropTypes.string.isRequired,
|
||||
imgControl: PropTypes.bool.isRequired,
|
||||
imgControlZoom: PropTypes.number.isRequired,
|
||||
imgControlMove: PropTypes.bool.isRequired,
|
||||
imgControlX: PropTypes.number.isRequired,
|
||||
imgControlY: PropTypes.number.isRequired,
|
||||
onClickClose: PropTypes.func.isRequired,
|
||||
onInputName: PropTypes.func.isRequired,
|
||||
onInputDesc: PropTypes.func.isRequired,
|
||||
onInputUrl: PropTypes.func.isRequired,
|
||||
onImgRead: PropTypes.func.isRequired,
|
||||
onImgZoom: PropTypes.func.isRequired,
|
||||
onImgMoveControl: PropTypes.func.isRequired,
|
||||
onImgMove: PropTypes.func.isRequired,
|
||||
onImgCancel: PropTypes.func.isRequired,
|
||||
onImgDone: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Submit
|
||||
|
@ -13,6 +13,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
.modalContentImgControl {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
@media (min-width: $desktop) {
|
||||
.modalContentImgControl {
|
||||
height: 512px;
|
||||
}
|
||||
}
|
||||
|
||||
.cntWithImgControl {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
@ -21,6 +39,10 @@
|
||||
border-bottom: 1px solid #f7f7f7;
|
||||
}
|
||||
|
||||
.withImgControl {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.block {
|
||||
padding: 0 16px 16px 16px;
|
||||
|
||||
@ -67,7 +89,6 @@
|
||||
border: 1px dashed #939ba1;
|
||||
margin: 16px auto;
|
||||
background: #eef2f5;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
color: #939ba1;
|
||||
@ -80,10 +101,20 @@
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: initial;
|
||||
border-radius: inherit;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
input.uploader {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.imgInfo {
|
||||
@ -125,6 +156,11 @@
|
||||
line-height: 22px;
|
||||
color: #939ba1;
|
||||
font-size: 15px;
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
@ -143,3 +179,88 @@
|
||||
background: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.imgControl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
|
||||
.imgCanvasCnt {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.imgCanvas {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
width: 100%;
|
||||
border-top: 1px solid #eef2f5;
|
||||
|
||||
.slider {
|
||||
height: 74px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
* {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.minZoom {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid #939ba1;
|
||||
border-radius: 3px;
|
||||
margin: auto 30px auto 15px;
|
||||
}
|
||||
|
||||
.maxZoom {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid #939ba1;
|
||||
border-radius: 3px;
|
||||
margin: auto 11px auto 26px;
|
||||
}
|
||||
}
|
||||
|
||||
.actionsCnt {
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
|
||||
.button {
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
border-radius: 22px;
|
||||
font-family: $font;
|
||||
padding: calculateRem(11) calculateRem(38);
|
||||
font-size: calculateRem(15);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cancelButton {
|
||||
color: $link-color;
|
||||
background: #eceffc;
|
||||
}
|
||||
|
||||
.doneButton {
|
||||
color: #fff;
|
||||
background: $link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,12 @@ const ON_INPUT_NAME = 'ON_INPUT_NAME'
|
||||
const ON_INPUT_DESC = 'ON_INPUT_DESC'
|
||||
const ON_INPUT_URL = 'ON_INPUT_URL'
|
||||
const ON_SELECT_CATEGORY = 'ON_SELECT_CATEGORY'
|
||||
const ON_IMG_READ = 'ON_IMG_READ'
|
||||
const ON_IMG_ZOOM = 'ON_IMG_ZOOM'
|
||||
const ON_IMG_MOVE_CONTROL = 'ON_IMG_MOVE_CONTROL'
|
||||
const ON_IMG_MOVE = 'ON_IMG_MOVE'
|
||||
const ON_IMG_CANCEL = 'ON_IMG_CANCEL'
|
||||
const ON_IMG_DONE = 'ON_IMG_DONE'
|
||||
|
||||
export const showSubmitAction = () => {
|
||||
window.location.hash = 'submit'
|
||||
@ -44,9 +50,45 @@ export const onSelectCategoryAction = category => ({
|
||||
payload: category,
|
||||
})
|
||||
|
||||
export const onImgReadAction = imgBase64 => ({
|
||||
type: ON_IMG_READ,
|
||||
payload: imgBase64,
|
||||
})
|
||||
|
||||
export const onImgZoomAction = zoom => ({
|
||||
type: ON_IMG_ZOOM,
|
||||
payload: zoom,
|
||||
})
|
||||
|
||||
export const onImgMoveControlAction = move => ({
|
||||
type: ON_IMG_MOVE_CONTROL,
|
||||
payload: move,
|
||||
})
|
||||
|
||||
export const onImgMoveAction = (x, y) => ({
|
||||
type: ON_IMG_MOVE,
|
||||
payload: { x, y },
|
||||
})
|
||||
|
||||
export const onImgCancelAction = () => ({
|
||||
type: ON_IMG_CANCEL,
|
||||
payload: null,
|
||||
})
|
||||
|
||||
export const onImgDoneAction = imgBase64 => ({
|
||||
type: ON_IMG_DONE,
|
||||
payload: imgBase64,
|
||||
})
|
||||
|
||||
const showSubmit = state => {
|
||||
return Object.assign({}, state, {
|
||||
visible: true,
|
||||
img: '',
|
||||
imgControl: false,
|
||||
imgControlZoom: 0,
|
||||
imgControlMove: false,
|
||||
imgControlX: 0,
|
||||
imgControlY: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@ -80,6 +122,50 @@ const onSelectCategory = (state, category) => {
|
||||
})
|
||||
}
|
||||
|
||||
const onImgRead = (state, imgBase64) => {
|
||||
return Object.assign({}, state, {
|
||||
img: imgBase64,
|
||||
imgControl: true,
|
||||
imgControlZoom: 0,
|
||||
imgControlMove: false,
|
||||
imgControlX: 0,
|
||||
imgControlY: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const onImgZoom = (state, zoom) => {
|
||||
return Object.assign({}, state, {
|
||||
imgControlZoom: zoom,
|
||||
})
|
||||
}
|
||||
|
||||
const onImgMoveControl = (state, move) => {
|
||||
return Object.assign({}, state, {
|
||||
imgControlMove: move,
|
||||
})
|
||||
}
|
||||
|
||||
const onImgMove = (state, payload) => {
|
||||
return Object.assign({}, state, {
|
||||
imgControlX: payload.x,
|
||||
imgControlY: payload.y,
|
||||
})
|
||||
}
|
||||
|
||||
const onImgCancel = state => {
|
||||
return Object.assign({}, state, {
|
||||
img: '',
|
||||
imgControl: false,
|
||||
})
|
||||
}
|
||||
|
||||
const onImgDone = (state, imgBase64) => {
|
||||
return Object.assign({}, state, {
|
||||
img: imgBase64,
|
||||
imgControl: false,
|
||||
})
|
||||
}
|
||||
|
||||
const map = {
|
||||
[SHOW_SUBMIT]: showSubmit,
|
||||
[CLOSE_SUBMIT]: closeSubmit,
|
||||
@ -87,6 +173,12 @@ const map = {
|
||||
[ON_INPUT_DESC]: onInputDesc,
|
||||
[ON_INPUT_URL]: onInputUrl,
|
||||
[ON_SELECT_CATEGORY]: onSelectCategory,
|
||||
[ON_IMG_READ]: onImgRead,
|
||||
[ON_IMG_ZOOM]: onImgZoom,
|
||||
[ON_IMG_MOVE_CONTROL]: onImgMoveControl,
|
||||
[ON_IMG_MOVE]: onImgMove,
|
||||
[ON_IMG_CANCEL]: onImgCancel,
|
||||
[ON_IMG_DONE]: onImgDone,
|
||||
}
|
||||
|
||||
export default reducerUtil(map, submitInitialState)
|
||||
|
13
src/modules/TransactionStatus/TransactionStatus.container.js
Normal file
13
src/modules/TransactionStatus/TransactionStatus.container.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { connect } from 'react-redux'
|
||||
import TransactionStatus from './TransactionStatus'
|
||||
import { hideAction } from './TransactionStatus.recuder'
|
||||
|
||||
const mapStateToProps = state => state.transactionStatus
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
hide: () => dispatch(hideAction()),
|
||||
})
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(TransactionStatus)
|
68
src/modules/TransactionStatus/TransactionStatus.jsx
Normal file
68
src/modules/TransactionStatus/TransactionStatus.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ReactImageFallback from 'react-image-fallback'
|
||||
import styles from './TransactionStatus.module.scss'
|
||||
import icon from '../../common/assets/images/icon.svg'
|
||||
import loadingSpinner from '../../common/assets/images/loading-spinner.svg'
|
||||
|
||||
class TransactionStatus extends React.Component {
|
||||
componentDidMount() {
|
||||
this.checkPublished()
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.checkPublished()
|
||||
}
|
||||
|
||||
checkPublished() {
|
||||
const { published, hide } = this.props
|
||||
if (published) {
|
||||
setTimeout(() => {
|
||||
hide()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dappName, dappUrl, published, progress } = this.props
|
||||
|
||||
return (
|
||||
<div className={`${styles.cnt} ${dappName !== '' ? styles.active : ''}`}>
|
||||
<ReactImageFallback
|
||||
className={styles.image}
|
||||
src={dappUrl}
|
||||
fallbackImage={icon}
|
||||
alt="App icon"
|
||||
/>
|
||||
<div className={styles.data}>
|
||||
<div className={styles.name}>{dappName}</div>
|
||||
<div className={styles.info}>
|
||||
Status is an open source mobile DApp browser and messenger build for
|
||||
#Etherium
|
||||
</div>
|
||||
{published && <div className={styles.status}>✓ Published</div>}
|
||||
{progress && (
|
||||
<div className={styles.status}>
|
||||
<img src={loadingSpinner} />
|
||||
Waiting for confirmation of the network...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TransactionStatus.defaultProps = {
|
||||
dapp: null,
|
||||
}
|
||||
|
||||
TransactionStatus.propTypes = {
|
||||
dappName: PropTypes.string.isRequired,
|
||||
dappUrl: PropTypes.string.isRequired,
|
||||
progress: PropTypes.bool.isRequired,
|
||||
published: PropTypes.bool.isRequired,
|
||||
hide: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default TransactionStatus
|
78
src/modules/TransactionStatus/TransactionStatus.module.scss
Normal file
78
src/modules/TransactionStatus/TransactionStatus.module.scss
Normal file
@ -0,0 +1,78 @@
|
||||
@import '../../common/styles/variables';
|
||||
|
||||
// @keyframes loading_rotate {
|
||||
// 100% {
|
||||
// transform: rotate(360deg);
|
||||
// }
|
||||
// }
|
||||
|
||||
.cnt {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
font-family: $font;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0px 4px 12px rgba(0, 34, 51, 0.08),
|
||||
0px 2px 4px rgba(0, 34, 51, 0.16);
|
||||
transform: translateY(-200%);
|
||||
|
||||
transition-property: transform;
|
||||
transition-duration: 0.4s;
|
||||
|
||||
.image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.name {
|
||||
line-height: 22px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.info {
|
||||
line-height: 18px;
|
||||
color: #939ba1;
|
||||
font-size: 13px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 15px;
|
||||
color: $link-color;
|
||||
font-size: 12px;
|
||||
|
||||
img {
|
||||
margin-right: 4px;
|
||||
// animation:loading_rotate 1s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cnt.active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@media (min-width: $desktop) {
|
||||
.cnt {
|
||||
width: 375px;
|
||||
right: 0;
|
||||
top: 16px;
|
||||
border-radius: 16px;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
21
src/modules/TransactionStatus/TransactionStatus.recuder.js
Normal file
21
src/modules/TransactionStatus/TransactionStatus.recuder.js
Normal file
@ -0,0 +1,21 @@
|
||||
import transactionStatusInitialState from '../../common/data/transaction-status'
|
||||
import reducerUtil from '../../common/utils/reducer'
|
||||
|
||||
const HIDE = 'HIDE'
|
||||
|
||||
export const hideAction = () => ({
|
||||
type: HIDE,
|
||||
payload: null,
|
||||
})
|
||||
|
||||
const hide = state => {
|
||||
return Object.assign({}, state, {
|
||||
dappName: '',
|
||||
})
|
||||
}
|
||||
|
||||
const map = {
|
||||
[HIDE]: hide,
|
||||
}
|
||||
|
||||
export default reducerUtil(map, transactionStatusInitialState)
|
3
src/modules/TransactionStatus/index.js
Normal file
3
src/modules/TransactionStatus/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import TransactionStatus from './TransactionStatus.container'
|
||||
|
||||
export default TransactionStatus
|
Loading…
x
Reference in New Issue
Block a user