add debug panel

This commit is contained in:
Andrea Franz 2020-05-29 13:07:00 +02:00
parent d163192572
commit 083398f82b
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
10 changed files with 80 additions and 84 deletions

View File

@ -10,15 +10,27 @@ export interface LayoutFlipCardAction {
flipped: boolean flipped: boolean
} }
export const LAYOUT_TOGGLE_DEBUG = "LAYOUT_TOGGLE_DEBUG";
export interface LayoutToggleDebugAction {
type: typeof LAYOUT_TOGGLE_DEBUG
open: boolean
}
export type LayoutActions = export type LayoutActions =
LayoutToggleSidebarAction | LayoutToggleSidebarAction |
LayoutFlipCardAction; LayoutFlipCardAction |
LayoutToggleDebugAction;
export const toggleSidebar = (open: boolean): LayoutToggleSidebarAction => ({ export const toggleSidebar = (open: boolean): LayoutToggleSidebarAction => ({
type: LAYOUT_TOGGLE_SIDEBAR, type: LAYOUT_TOGGLE_SIDEBAR,
open, open,
}); });
export const toggleDebug = (open: boolean): LayoutToggleDebugAction => ({
type: LAYOUT_TOGGLE_DEBUG,
open,
});
export const flipCard = (flipped: boolean): LayoutFlipCardAction => ({ export const flipCard = (flipped: boolean): LayoutFlipCardAction => ({
type: LAYOUT_FLIP_CARD, type: LAYOUT_FLIP_CARD,
flipped, flipped,

View File

@ -6,12 +6,7 @@ import { sha3 } from "web3-utils";
import { recoverTypedSignature } from 'eth-sig-util'; import { recoverTypedSignature } from 'eth-sig-util';
import { Web3Type } from "../actions/web3"; import { Web3Type } from "../actions/web3";
import { KECCAK_EMPTY_STRING } from '../utils'; import { KECCAK_EMPTY_STRING } from '../utils';
import { debug } from "./debug";
const sleep = (ms: number) => {
return new Promise(resolve => {
window.setTimeout(resolve, ms);
});
}
interface RedeemMessage { interface RedeemMessage {
receiver: string receiver: string
@ -22,7 +17,7 @@ interface RedeemMessage {
interface SignRedeemResponse { interface SignRedeemResponse {
sig: string sig: string
address: string signer: string
} }
export const ERROR_REDEEMING = "ERROR_REDEEMING"; export const ERROR_REDEEMING = "ERROR_REDEEMING";
@ -105,6 +100,11 @@ export const redeem = (bucketAddress: string, recipientAddress: string, cleanCod
const bucket = newBucketContract(bucketAddress); const bucket = newBucketContract(bucketAddress);
const account = state.web3.account; const account = state.web3.account;
if (account === undefined) {
//FIXME: show error?
return;
}
const block = await config.web3!.eth.getBlock("latest"); const block = await config.web3!.eth.getBlock("latest");
const message = { const message = {
@ -182,7 +182,7 @@ async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: s
}; };
if (web3Type === Web3Type.Status) { if (web3Type === Web3Type.Status) {
return signWithKeycard(signer, data); return signWithKeycard(data);
} else { } else {
return signWithWeb3(signer, data); return signWithWeb3(signer, data);
} }

View File

@ -1,4 +1,6 @@
import { RootState } from '../reducers'; import { RootState } from '../reducers';
import ERC20BucketFactory from '../embarkArtifacts/contracts/ERC20BucketFactory';
import NFTBucketFactory from '../embarkArtifacts/contracts/NFTBucketFactory';
import ERC20Bucket from '../embarkArtifacts/contracts/ERC20Bucket'; import ERC20Bucket from '../embarkArtifacts/contracts/ERC20Bucket';
import Bucket from '../embarkArtifacts/contracts/Bucket'; import Bucket from '../embarkArtifacts/contracts/Bucket';
import IERC20Detailed from '../embarkArtifacts/contracts/IERC20Detailed'; import IERC20Detailed from '../embarkArtifacts/contracts/IERC20Detailed';
@ -6,6 +8,7 @@ import IERC721Metadata from '../embarkArtifacts/contracts/IERC721Metadata';
import { config } from "../config"; import { config } from "../config";
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { ZERO_ADDRESS } from "../utils"; import { ZERO_ADDRESS } from "../utils";
import { debug } from "./debug";
export const ERROR_REDEEMABLE_NOT_FOUND = "ERROR_REDEEMABLE_NOT_FOUND"; export const ERROR_REDEEMABLE_NOT_FOUND = "ERROR_REDEEMABLE_NOT_FOUND";
export interface ErrRedeemableNotFound { export interface ErrRedeemableNotFound {
@ -176,6 +179,10 @@ export const newERC20BucketContract = (address: string) => {
export const loadRedeemable = (bucketAddress: string, recipientAddress: string) => { export const loadRedeemable = (bucketAddress: string, recipientAddress: string) => {
return async (dispatch: Dispatch, getState: () => RootState) => { return async (dispatch: Dispatch, getState: () => RootState) => {
dispatch(debug(`erc20 factory address: ${ERC20BucketFactory.address}`));
dispatch(debug(`nft factory address: ${NFTBucketFactory.address}`));
dispatch(debug(`bucket address: ${bucketAddress}`));
dispatch(debug(`recipient address: ${recipientAddress}`));
dispatch(loadingRedeemable(bucketAddress, recipientAddress)); dispatch(loadingRedeemable(bucketAddress, recipientAddress));
const bucket = newBucketContract(bucketAddress); const bucket = newBucketContract(bucketAddress);
bucket.methods.expirationTime().call().then((expirationTime: number) => { bucket.methods.expirationTime().call().then((expirationTime: number) => {
@ -231,9 +238,11 @@ export const loadERC20Token = (bucket: any, data: string, recipient: string) =>
const symbol = await erc20.methods.symbol().call(); const symbol = await erc20.methods.symbol().call();
const decimals = parseInt(await erc20.methods.decimals().call()); const decimals = parseInt(await erc20.methods.decimals().call());
dispatch(debug(`erc20 token: ${symbol} ${address}`));
dispatch(tokenLoaded({symbol, decimals})); dispatch(tokenLoaded({symbol, decimals}));
}).catch((err: string) => { }).catch((err: string) => {
//FIXME: manage error //FIXME: manage error
dispatch(debug(`error loading token: ${err})`));
console.error("ERROR: ", err); console.error("ERROR: ", err);
}) })
} }
@ -251,6 +260,8 @@ export const loadNFTToken = (bucket: any, data: string, recipient: string) => {
dispatch(tokenLoaded({symbol, tokenURI, metadata: undefined})); dispatch(tokenLoaded({symbol, tokenURI, metadata: undefined}));
dispatch(loadingTokenMetadata(address, recipient)) dispatch(loadingTokenMetadata(address, recipient))
dispatch(debug(`nft token: ${symbol} ${address}`));
fetch(tokenURI) fetch(tokenURI)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
@ -261,10 +272,12 @@ export const loadNFTToken = (bucket: any, data: string, recipient: string) => {
})); }));
}) })
.catch((err: string) => { .catch((err: string) => {
dispatch(debug(`error loading metadata: ${err})`));
//FIXME: manage error //FIXME: manage error
console.error("ERROR: ", err); console.error("ERROR: ", err);
}); });
}).catch((err: string) => { }).catch((err: string) => {
dispatch(debug(`error loading token: ${err})`));
//FIXME: manage error //FIXME: manage error
console.error("ERROR: ", err); console.error("ERROR: ", err);
}) })

View File

@ -4,6 +4,7 @@ import {
Dispatch, Dispatch,
} from 'redux'; } from 'redux';
import { RootState } from '../reducers'; import { RootState } from '../reducers';
import { debug } from "./debug";
export const VALID_NETWORK_NAME = "Ropsten"; export const VALID_NETWORK_NAME = "Ropsten";
export const VALID_NETWORK_ID = 3; export const VALID_NETWORK_ID = 3;
@ -86,6 +87,7 @@ export const initializeWeb3 = () => {
return; return;
} }
dispatch(debug(`network id: ${id}`))
dispatch(web3NetworkIDLoaded(id)) dispatch(web3NetworkIDLoaded(id))
dispatch<any>(loadAddress()); dispatch<any>(loadAddress());
}); });
@ -98,6 +100,7 @@ export const initializeWeb3 = () => {
const t: Web3Type = w.ethereum.isStatus ? Web3Type.Status : Web3Type.Generic; const t: Web3Type = w.ethereum.isStatus ? Web3Type.Status : Web3Type.Generic;
dispatch(web3Initialized(t)); dispatch(web3Initialized(t));
config.web3!.eth.net.getId().then((id: number) => { config.web3!.eth.net.getId().then((id: number) => {
dispatch(debug(`network id: ${id}`))
dispatch(web3NetworkIDLoaded(id)) dispatch(web3NetworkIDLoaded(id))
dispatch<any>(loadAddress()); dispatch<any>(loadAddress());
}) })
@ -114,6 +117,7 @@ export const initializeWeb3 = () => {
const loadAddress = () => { const loadAddress = () => {
return (dispatch: Dispatch, getState: () => RootState) => { return (dispatch: Dispatch, getState: () => RootState) => {
config.web3!.eth.getAccounts().then((accounts: string[]) => { config.web3!.eth.getAccounts().then((accounts: string[]) => {
dispatch(debug(`current account: ${accounts[0]}`));
dispatch(accountLoaded(accounts[0])); dispatch(accountLoaded(accounts[0]));
}); });
}; };

View File

@ -9,19 +9,6 @@ import {
import { Web3Type } from "../actions/web3"; import { Web3Type } from "../actions/web3";
import "../styles/Layout.scss"; import "../styles/Layout.scss";
const web3Type = (t: Web3Type) => {
switch (t) {
case Web3Type.None:
return "not a web3 browser";
case Web3Type.Generic:
return "generic web3 browser";
case Web3Type.Remote:
return "remote web3 node";
case Web3Type.Status:
return "status web3 browser";
}
}
export default function(ownProps: any) { export default function(ownProps: any) {
const props = useSelector((state: RootState) => { const props = useSelector((state: RootState) => {
return { return {
@ -40,20 +27,11 @@ export default function(ownProps: any) {
}); });
return <div className="main"> return <div className="main">
<div className={sidebarClass}>
<div className="inner">
Network ID: {props.networkID} <br />
Factory: {ERC20BucketFactory.address} <br />
Account: {props.account} <br />
Web3 Type: {web3Type(props.type)} <br />
</div>
</div>
{props.error && <div className={classNames({ paper: true, error: true })}> {props.error && <div className={classNames({ paper: true, error: true })}>
{props.error} {props.error}
</div>} </div>}
{!props.initialized && <div className={classNames({ paper: true })}> {!props.error && !props.initialized && <div className={classNames({ paper: true })}>
initializing... initializing...
</div>} </div>}

View File

@ -27,12 +27,17 @@ import {
ERROR_REDEEMING, ERROR_REDEEMING,
ERROR_WRONG_SIGNER, ERROR_WRONG_SIGNER,
} from '../actions/redeem'; } from '../actions/redeem';
import { flipCard } from "../actions/layout"; import {
flipCard,
toggleDebug,
} from "../actions/layout";
import "../styles/Redeemable.scss"; import "../styles/Redeemable.scss";
import "../styles/Debug.scss";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { import {
faUndo as flipIcon, faUndo as flipIcon,
faUndo as unflipIcon, faUndo as unflipIcon,
faWrench as debugIcon,
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
@ -55,7 +60,7 @@ const redeemErrorMessage = (error: RedeemErrors): string => {
return `wrong signer. expected signature from ${error.expected}, got signature from ${error.actual}`; return `wrong signer. expected signature from ${error.expected}, got signature from ${error.actual}`;
case ERROR_REDEEMING: case ERROR_REDEEMING:
return `redeem error: ${error.message}`; return error.message;
default: default:
return "something went wrong"; return "something went wrong";
@ -99,6 +104,8 @@ export default function(ownProps: any) {
redeemError: state.redeem.error, redeemError: state.redeem.error,
redeemTxHash: state.redeem.txHash, redeemTxHash: state.redeem.txHash,
cardFlipped: state.layout.cardFlipped, cardFlipped: state.layout.cardFlipped,
debugLines: state.debug.lines,
debugOpen: state.layout.debugOpen,
} }
}, shallowEqual); }, shallowEqual);
@ -179,7 +186,8 @@ export default function(ownProps: any) {
const backClass = classNames({ side: true, back: true }); const backClass = classNames({ side: true, back: true });
return <div className={cardClass}> return <div>
<div className={cardClass}>
<div className={frontClass}> <div className={frontClass}>
<div className="header"> <div className="header">
<button className="flip" onClick={ () => { dispatch(flipCard(true)) } }> <button className="flip" onClick={ () => { dispatch(flipCard(true)) } }>
@ -195,7 +203,7 @@ export default function(ownProps: any) {
{tokenContent} {tokenContent}
</div> </div>
{props.redeemError && <div className="error"> {props.redeemError && <div className="error">
Error: {redeemErrorMessage(props.redeemError)} {redeemErrorMessage(props.redeemError)}
</div>} </div>}
{props.redeemTxHash && <div className="success"> {props.redeemTxHash && <div className="success">
Done! Tx Hash: {props.redeemTxHash} Done! Tx Hash: {props.redeemTxHash}
@ -241,5 +249,17 @@ export default function(ownProps: any) {
<div className="footer"> <div className="footer">
</div> </div>
</div> </div>
</div>
<div className="debug">
<FontAwesomeIcon
onClick={() => dispatch(toggleDebug(!props.debugOpen)) }
icon={debugIcon}
className="btn" />
{props.debugOpen && <ul>
{props.debugLines.map((text: string, i: number) => (<li key={i}>
{text}
</li>))}
</ul>}
</div>
</div>; </div>;
} }

View File

@ -17,12 +17,17 @@ import {
LayoutState, LayoutState,
layoutReducer, layoutReducer,
} from './layout'; } from './layout';
import {
DebugState,
debugReducer,
} from './debug';
export interface RootState { export interface RootState {
web3: Web3State, web3: Web3State,
redeemable: RedeemableState, redeemable: RedeemableState,
redeem: RedeemState, redeem: RedeemState,
layout: LayoutState, layout: LayoutState,
debug: DebugState,
} }
export default function(history: History) { export default function(history: History) {
@ -32,5 +37,6 @@ export default function(history: History) {
redeemable: redeemableReducer, redeemable: redeemableReducer,
redeem: redeemReducer, redeem: redeemReducer,
layout: layoutReducer, layout: layoutReducer,
debug: debugReducer,
}); });
} }

View File

@ -1,17 +1,20 @@
import { import {
LayoutActions, LayoutActions,
LAYOUT_TOGGLE_SIDEBAR, LAYOUT_TOGGLE_SIDEBAR,
LAYOUT_TOGGLE_DEBUG,
LAYOUT_FLIP_CARD, LAYOUT_FLIP_CARD,
} from "../actions/layout"; } from "../actions/layout";
export interface LayoutState { export interface LayoutState {
sidebarOpen: boolean, sidebarOpen: boolean,
cardFlipped: boolean, cardFlipped: boolean,
debugOpen: boolean,
} }
const initialState: LayoutState = { const initialState: LayoutState = {
sidebarOpen: false, sidebarOpen: false,
cardFlipped: false, cardFlipped: false,
debugOpen: false,
} }
export const layoutReducer = (state: LayoutState = initialState, action: LayoutActions): LayoutState => { export const layoutReducer = (state: LayoutState = initialState, action: LayoutActions): LayoutState => {
@ -22,6 +25,12 @@ export const layoutReducer = (state: LayoutState = initialState, action: LayoutA
sidebarOpen: action.open, sidebarOpen: action.open,
}; };
case LAYOUT_TOGGLE_DEBUG:
return {
...state,
debugOpen: action.open,
};
case LAYOUT_FLIP_CARD: case LAYOUT_FLIP_CARD:
return { return {
...state, ...state,

View File

@ -16,53 +16,6 @@ body {
font-family: "Roboto", "Helvetica", "Arial", sans-serif; font-family: "Roboto", "Helvetica", "Arial", sans-serif;
} }
.sidebar {
z-index: 9999;
background: #fff;
position: absolute;
top: 0;
left: -100vw;
min-height: 100vh;
width: 100vw;
&.open {
animation-duration: 0.2s;
animation-name: slidein;
left: 0;
}
&.closed {
animation-duration: 0.2s;
animation-name: slideout;
left: -100vw;
}
.inner {
padding: 24px;
word-break: break-all;
}
}
@keyframes slidein {
from {
left: -100vw;
}
to {
left: 0;
}
}
@keyframes slideout {
from {
left: 0;
}
to {
left: -100vw;
}
}
.main { .main {
margin-top: 50px; margin-top: 50px;

View File

@ -30,10 +30,10 @@
margin: 0 auto; margin: 0 auto;
perspective:1000px; perspective:1000px;
border-radius: 8px;
position: relative; position: relative;
background: #fff; background: #fff;
box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12); box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12);
border-radius: 8px;
transform-style: preserve-3d; transform-style: preserve-3d;
transition: transform 1.0s; transition: transform 1.0s;
height: 500px; height: 500px;
@ -43,6 +43,7 @@
} }
.side { .side {
border-radius: 8px;
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;