Merge pull request #521 from c9s/feature/web-qrcode

feature: add qrcode for mobile app to scan
This commit is contained in:
Yo-An Lin 2022-04-14 10:32:27 +08:00 committed by GitHub
commit 068e6a70e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 9963 deletions

View File

@ -8,6 +8,12 @@ export function ping(cb) {
}); });
} }
export function queryOutboundIP(cb) {
return axios.get(baseURL + '/api/outbound-ip').then(response => {
cb(response.data.outboundIP)
});
}
export function querySyncStatus(cb) { export function querySyncStatus(cb) {
return axios.get(baseURL + '/api/environment/syncing').then(response => { return axios.get(baseURL + '/api/environment/syncing').then(response => {
cb(response.data.syncing) cb(response.data.syncing)

View File

@ -47,7 +47,7 @@ export default function TradingVolumePanel() {
</Tabs> </Tabs>
</Grid> </Grid>
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Grid container justify={"flex-end"}> <Grid container justifyContent={"flex-end"}>
<Tabs value={segment} <Tabs value={segment}
onChange={handleSegmentChange} onChange={handleSegmentChange}
indicatorColor="primary" indicatorColor="primary"

View File

@ -22,14 +22,13 @@ const useStyles = makeStyles((theme) => ({
appBarSpacer: theme.mixins.toolbar, appBarSpacer: theme.mixins.toolbar,
})); }));
export default function PlainLayout({children}) { export default function PlainLayout(props) {
const classes = useStyles(); const classes = useStyles();
return <div className={classes.root}> return <div className={classes.root}>
<AppBar className={classes.appBar}> <AppBar className={classes.appBar}>
<Toolbar> <Toolbar>
<Typography variant="h6" className={classes.title}> <Typography variant="h6" className={classes.title}>
BBGO Setup Wizard { props && props.title ? props.title : "BBGO Setup Wizard" }
</Typography> </Typography>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
@ -37,7 +36,7 @@ export default function PlainLayout({children}) {
<main className={classes.content}> <main className={classes.content}>
<div className={classes.appBarSpacer}/> <div className={classes.appBarSpacer}/>
<Container> <Container>
{children} {props.children}
</Container> </Container>
</main> </main>
</div>; </div>;

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,16 @@
"@nivo/bar": "^0.73.1", "@nivo/bar": "^0.73.1",
"@nivo/core": "^0.73.0", "@nivo/core": "^0.73.0",
"@nivo/pie": "^0.73.0", "@nivo/pie": "^0.73.0",
"@usedapp/core": "0.5.4",
"axios": "^0.22.0", "axios": "^0.22.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"next": "^11.1.2", "next": "^11.1.2",
"qrcode.react": "^3.0.1",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-number-format": "^4.4.4", "react-number-format": "^4.4.4",
"react-spring": "^9.3.0", "react-spring": "^9.3.0"
"@usedapp/core": "0.5.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^14.14.22", "@types/node": "^14.14.22",

View File

@ -0,0 +1,57 @@
import React, {useEffect, useState} from 'react';
import {makeStyles} from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import PlainLayout from '../../layouts/PlainLayout';
import {QRCodeSVG} from 'qrcode.react';
import {queryOutboundIP} from '../../api/bbgo';
const useStyles = makeStyles((theme) => ({
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
dataGridContainer: {
display: 'flex',
textAlign: 'center',
alignItems: 'center',
alignContent: 'center',
height: 320,
}
}));
function fetchConnectUrl(cb) {
return queryOutboundIP((outboundIP) => {
cb(window.location.protocol + "//" + outboundIP + ":" + window.location.port)
})
}
export default function Connect() {
const classes = useStyles();
const [connectUrl, setConnectUrl] = useState([])
useEffect(() => {
fetchConnectUrl(function (url) {
setConnectUrl(url)
})
}, [])
return (
<PlainLayout title={"Connect"}>
<Paper className={classes.paper}>
<Typography variant="h4" gutterBottom>
Sign In Using QR Codes
</Typography>
<div className={classes.dataGridContainer}>
<QRCodeSVG
size={160}
style={{flexGrow: 1}}
value={connectUrl}/>
</div>
</Paper>
</PlainLayout>
);
}

View File

@ -89,7 +89,7 @@ export default function Home() {
<div className={classes.grid}> <div className={classes.grid}>
<Grid container <Grid container
direction="row" direction="row"
justify="space-around" justifyContent="space-around"
alignItems="flex-start" alignItems="flex-start"
spacing={1}> spacing={1}>
<Grid item xs={12} md={8}> <Grid item xs={12} md={8}>

View File

@ -3247,6 +3247,11 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qrcode.react@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.0.1.tgz#0cb1d7cfdf3955737fbd3509c193985795ca0612"
integrity sha512-uCNm16ClMCrdM2R20c/zqmdwHcbMQf3K7ey39EiK/UgEKbqWeM0iH2QxW3iDVFzjQKFzH23ICgOyG4gNsJ0/gw==
querystring-es3@0.2.1, querystring-es3@^0.2.0: querystring-es3@0.2.1, querystring-es3@^0.2.0:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"

View File

@ -88,6 +88,19 @@ func (s *Server) newEngine() *gin.Engine {
}) })
}) })
r.GET("/api/outbound-ip", func(c *gin.Context) {
outboundIP, err := GetOutboundIP()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
c.JSON(http.StatusOK, gin.H{
"outboundIP": outboundIP.String(),
})
})
r.GET("/api/trades", func(c *gin.Context) { r.GET("/api/trades", func(c *gin.Context) {
if s.Environ.TradeService == nil { if s.Environ.TradeService == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"})
@ -621,3 +634,14 @@ func listenAndServe(srv *http.Server) error {
return nil return nil
} }
func GetOutboundIP() (net.IP, error) {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return nil, err
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP, nil
}