Add eslint, prettier, husky. Fix linting errors

This commit is contained in:
finned-palmer 2021-06-19 12:47:24 -05:00
parent 7ac718a2c7
commit fa797d411c
12 changed files with 2171 additions and 234 deletions

View File

@ -0,0 +1,27 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 11,
"sourceType": "module"
},
"plugins": ["react"],
"rules": {
"react/prop-types": 0
},
"settings": {
"react": {
"version": "detect"
}
}
}

1
pkg/btc-wallet/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.eslintcache

1
pkg/btc-wallet/.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
cd pkg/btc-wallet && npx lint-staged src

View File

@ -0,0 +1,4 @@
{
"semi": true,
"singleQuote": true
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,8 @@
"scripts": {
"start": "webpack-dev-server --config config/webpack.dev.js",
"build:dev": "cross-env NODE_ENV=production webpack --config config/webpack.dev.js",
"build:prod": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"
"build:prod": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js",
"prepare": "cd ../.. && husky install pkg/btc-wallet/.husky"
},
"author": "Tlon Corp",
"license": "MIT",
@ -24,8 +25,14 @@
"babel-plugin-root-import": "^6.5.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.2",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-react": "^7.24.0",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.2.0",
"husky": "^6.0.0",
"lint-staged": "^11.0.0",
"prettier": "^2.3.1",
"react-hot-loader": "^4.12.21",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
@ -71,5 +78,9 @@
},
"resolutions": {
"natives": "1.1.3"
},
"lint-staged": {
"*.js": "eslint --cache --fix",
"*.{js,css,md}": "prettier --write"
}
}

View File

@ -7,10 +7,13 @@ class UrbitApi {
this.bindPaths = [];
}
bind(path, method, ship = this.ship, appl = "btc-wallet", success, fail) {
bind(path, method, ship = this.ship, appl = 'btc-wallet', success, fail) {
this.bindPaths = _.uniq([...this.bindPaths, path]);
window.subscriptionId = this.channel.subscribe(ship, appl, path,
window.subscriptionId = this.channel.subscribe(
ship,
appl,
path,
(err) => {
fail(err);
},
@ -19,32 +22,38 @@ class UrbitApi {
data: event,
from: {
ship,
path
}
path,
},
});
},
(err) => {
fail(err);
});
}
);
}
btcWalletCommand(data) {
return this.action("btc-wallet", "btc-wallet-command", data);
return this.action('btc-wallet', 'btc-wallet-command', data);
}
settingsEvent(data) {
return this.action("settings-store", "settings-event", data);
return this.action('settings-store', 'settings-event', data);
}
action(appl, mark, data) {
return new Promise((resolve, reject) => {
this.channel.poke(ship, appl, mark, data,
this.channel.poke(
this.ship,
appl,
mark,
data,
(json) => {
resolve(json);
},
(err) => {
reject(err);
});
}
);
});
}
}

View File

@ -1,17 +1,9 @@
import React, { Component } from 'react';
import {
Box,
Icon,
Row,
Text,
Button,
Col,
} from '@tlon/indigo-react';
import Send from './send.js'
import CurrencyPicker from './currencyPicker.js'
import { currencyToSats, satsToCurrency } from '../../lib/util.js';
import { Row, Text, Button, Col } from '@tlon/indigo-react';
import Send from './send.js';
import CurrencyPicker from './currencyPicker.js';
import { satsToCurrency } from '../../lib/util.js';
import { store } from '../../store.js';
export default class Balance extends Component {
constructor(props) {
@ -21,7 +13,7 @@ export default class Balance extends Component {
sending: false,
copiedButton: false,
copiedString: false,
}
};
this.copyAddress = this.copyAddress.bind(this);
}
@ -29,110 +21,149 @@ export default class Balance extends Component {
copyAddress(arg) {
let address = this.props.state.address;
navigator.clipboard.writeText(address);
this.props.api.btcWalletCommand({'gen-new-address': null});
this.props.api.btcWalletCommand({ 'gen-new-address': null });
if (arg === 'button'){
this.setState({copiedButton: true});
setTimeout(() => { this.setState({copiedButton: false}); }, 2000);
if (arg === 'button') {
this.setState({ copiedButton: true });
setTimeout(() => {
this.setState({ copiedButton: false });
}, 2000);
} else if (arg === 'string') {
this.setState({copiedString: true});
setTimeout(() => { this.setState({copiedString: false}); }, 2000);
this.setState({ copiedString: true });
setTimeout(() => {
this.setState({ copiedString: false });
}, 2000);
}
}
render() {
const sats = (this.props.state.confirmedBalance || 0);
const sats = this.props.state.confirmedBalance || 0;
const unconfirmedSats = this.props.state.unconfirmedBalance;
const unconfirmedString = unconfirmedSats ? ` (${unconfirmedSats}) ` : '';
const denomination = this.props.state.denomination;
const value = satsToCurrency(sats, denomination, this.props.state.currencyRates);
const sendDisabled = (sats === 0);
const addressText = (this.props.state.address === null) ? '' :
this.props.state.address.slice(0, 6) + '...' +
this.props.state.address.slice(-6);
const value = satsToCurrency(
sats,
denomination,
this.props.state.currencyRates
);
const sendDisabled = sats === 0;
const addressText =
this.props.state.address === null
? ''
: this.props.state.address.slice(0, 6) +
'...' +
this.props.state.address.slice(-6);
const conversion = this.props.state.currencyRates[denomination].last;
return (
<>
{this.state.sending ?
<Send
state={this.props.state}
api={api}
psbt={this.props.state.psbt}
fee={this.props.state.fee}
currencyRates={this.props.state.currencyRates}
shipWallets={this.props.state.shipWallets}
value={value}
denomination={denomination}
sats={sats}
conversion={conversion}
network={this.props.network}
error={this.props.state.error}
stopSending={() => {
this.setState({sending: false});
store.handleEvent({data: {psbt: '', fee: 0, error: '', "broadcast-fail": null}});
}}
/> :
<Col
height="400px"
width='100%'
backgroundColor="white"
borderRadius="48px"
justifyContent="space-between"
mb={5}
p={5}
>
<Row justifyContent="space-between">
<Text color="orange" fontSize={1}>Balance</Text>
<Text color="lightGray" fontSize="14px" mono style={{cursor: "pointer"}}
onClick={() => {this.copyAddress('string')}}>
{this.state.copiedString ? "copied" : addressText}
</Text>
<CurrencyPicker
api={this.props.api}
denomination={denomination}
currencies={this.props.state.currencyRates}
/>
</Row>
<Col justifyContent="center" alignItems="center">
<Text fontSize="40px" color="orange" style={{whiteSpace: "nowrap"}} >
{value}
</Text>
<Text fontSize={1} color="orange">{`${sats}${unconfirmedString} sats`}</Text>
</Col>
<Row flexDirection="row-reverse">
<Button children="Send"
disabled={sendDisabled}
fontSize={1}
fontWeight="bold"
color={sendDisabled ? "lighterGray" : "white"}
backgroundColor={sendDisabled ? "veryLightGray" : "orange"}
style={{cursor: sendDisabled ? "default" : "pointer" }}
borderColor="none"
borderRadius="24px"
height="48px"
onClick={() => this.setState({sending: true})}
/>
<Button children={(this.state.copiedButton) ? "Address Copied!" : "Copy Address"}
mr={3}
disabled={this.state.copiedButton}
fontSize={1}
fontWeight="bold"
color={(this.state.copiedButton) ? "green" : "orange"}
backgroundColor={(this.state.copiedButton) ? "veryLightGreen" : "midOrange" }
style={{cursor: (this.state.copiedButton) ? "default" : "pointer"}}
borderColor="none"
borderRadius="24px"
height="48px"
onClick={() => {this.copyAddress('button')}}
/>
</Row>
{this.state.sending ? (
<Send
state={this.props.state}
api={this.props.api}
psbt={this.props.state.psbt}
fee={this.props.state.fee}
currencyRates={this.props.state.currencyRates}
shipWallets={this.props.state.shipWallets}
value={value}
denomination={denomination}
sats={sats}
conversion={conversion}
network={this.props.network}
error={this.props.state.error}
stopSending={() => {
this.setState({ sending: false });
store.handleEvent({
data: { psbt: '', fee: 0, error: '', 'broadcast-fail': null },
});
}}
/>
) : (
<Col
height="400px"
width="100%"
backgroundColor="white"
borderRadius="48px"
justifyContent="space-between"
mb={5}
p={5}
>
<Row justifyContent="space-between">
<Text color="orange" fontSize={1}>
Balance
</Text>
<Text
color="lightGray"
fontSize="14px"
mono
style={{ cursor: 'pointer' }}
onClick={() => {
this.copyAddress('string');
}}
>
{this.state.copiedString ? 'copied' : addressText}
</Text>
<CurrencyPicker
api={this.props.api}
denomination={denomination}
currencies={this.props.state.currencyRates}
/>
</Row>
<Col justifyContent="center" alignItems="center">
<Text
fontSize="40px"
color="orange"
style={{ whiteSpace: 'nowrap' }}
>
{value}
</Text>
<Text
fontSize={1}
color="orange"
>{`${sats}${unconfirmedString} sats`}</Text>
</Col>
<Row flexDirection="row-reverse">
<Button
disabled={sendDisabled}
fontSize={1}
fontWeight="bold"
color={sendDisabled ? 'lighterGray' : 'white'}
backgroundColor={sendDisabled ? 'veryLightGray' : 'orange'}
style={{ cursor: sendDisabled ? 'default' : 'pointer' }}
borderColor="none"
borderRadius="24px"
height="48px"
onClick={() => this.setState({ sending: true })}
>
Send
</Button>
<Button
mr={3}
disabled={this.state.copiedButton}
fontSize={1}
fontWeight="bold"
color={this.state.copiedButton ? 'green' : 'orange'}
backgroundColor={
this.state.copiedButton ? 'veryLightGreen' : 'midOrange'
}
style={{
cursor: this.state.copiedButton ? 'default' : 'pointer',
}}
borderColor="none"
borderRadius="24px"
height="48px"
onClick={() => {
this.copyAddress('button');
}}
>
{this.state.copiedButton ? 'Address Copied!' : 'Copy Address'}
</Button>
</Row>
</Col>
}
)}
</>
);
}

View File

@ -6,7 +6,6 @@ import {
StatelessTextInput,
Icon,
Row,
Input,
LoadingSpinner,
} from '@tlon/indigo-react';
@ -23,9 +22,9 @@ export default class ProviderModal extends Component {
ready: false,
provider: null,
connecting: false,
}
};
this.checkProvider = this.checkProvider.bind(this);
this.checkProvider = this.checkProvider.bind(this);
this.submitProvider = this.submitProvider.bind(this);
}
@ -38,77 +37,88 @@ export default class ProviderModal extends Component {
if (isValidPatp(provider)) {
let command = {
"check-provider": provider
}
'check-provider': provider,
};
potentialProvider = provider;
checkingProvider = true;
this.props.api.btcWalletCommand(command);
setTimeout(() => {
this.setState({providerFailed: true, checkingProvider: false});
this.setState({ providerFailed: true, checkingProvider: false });
}, 5000);
}
this.setState({provider, ready, checkingProvider, potentialProvider});
this.setState({ provider, ready, checkingProvider, potentialProvider });
}
componentDidUpdate(prevProps, prevState){
if (!this.state.ready){
componentDidUpdate() {
if (!this.state.ready) {
if (this.props.providerPerms[this.state.provider]) {
this.setState({ready: true, checkingProvider: false, providerFailed: false});
this.setState({
ready: true,
checkingProvider: false,
providerFailed: false,
});
}
}
}
submitProvider(e){
if (this.state.ready){
submitProvider() {
if (this.state.ready) {
let command = {
"set-provider": this.state.provider
}
'set-provider': this.state.provider,
};
this.props.api.btcWalletCommand(command);
this.setState({connecting: true});
this.setState({ connecting: true });
}
}
render() {
let workingNode = null;
let workingColor = null;
let workingBg = null;
if (this.state.ready) {
workingColor = "green";
workingBg = "veryLightGreen"
workingNode =
workingColor = 'green';
workingBg = 'veryLightGreen';
workingNode = (
<Box mt={3}>
<Text fontSize="14px" color="green">
{this.state.provider} is a working provider node
</Text>
</Box>
);
} else if (this.state.providerFailed) {
workingColor = "red";
workingBg = "veryLightRed"
workingNode =
workingColor = 'red';
workingBg = 'veryLightRed';
workingNode = (
<Box mt={3}>
<Text fontSize="14px" color="red">
{this.state.potentialProvider} is not a working provider node
</Text>
</Box>
);
}
return (
<Box
width="100%"
height="100%"
padding={3}
>
<Box width="100%" height="100%" padding={3}>
<Row>
<Icon icon="Bitcoin" mr={2}/>
<Icon icon="Bitcoin" mr={2} />
<Text fontSize="14px" fontWeight="bold">
Step 1 of 2: Set up Bitcoin Provider Node
</Text>
</Row>
<Box mt={3}>
<Text fontSize="14px" fontWeight="regular" color="gray">
In order to perform Bitcoin transaction in Landscape, you'll need to set a provider node. A provider node is an urbit which maintains a synced Bitcoin ledger.
<a fontSize="14px" target="_blank" href="https://urbit.org/bitcoin-wallet"> Learn More</a>
In order to perform Bitcoin transaction in Landscape, you&apos;ll
need to set a provider node. A provider node is an urbit which
maintains a synced Bitcoin ledger.
<a
fontSize="14px"
target="_blank"
href="https://urbit.org/bitcoin-wallet"
rel="noreferrer"
>
{' '}
Learn More
</a>
</Text>
</Box>
<Box mt={3} mb={2}>
@ -132,7 +142,7 @@ export default class ProviderModal extends Component {
borderColor={workingColor}
onChange={this.checkProvider}
/>
{(this.state.checkingProvider) ? <LoadingSpinner/> : null}
{this.state.checkingProvider ? <LoadingSpinner /> : null}
</Row>
{workingNode}
<Row alignItems="center" mt={3}>
@ -140,12 +150,15 @@ export default class ProviderModal extends Component {
mr={2}
primary
disabled={!this.state.ready}
children="Set Peer Node"
fontSize="14px"
style={{cursor: this.state.ready ? "pointer" : "default"}}
onClick={() => {this.submitProvider(this.state.provider)}}
/>
{(this.state.connecting) ? <LoadingSpinner/> : null}
style={{ cursor: this.state.ready ? 'pointer' : 'default' }}
onClick={() => {
this.submitProvider(this.state.provider);
}}
>
Set Peer Node
</Button>
{this.state.connecting ? <LoadingSpinner /> : null}
</Row>
</Box>
);

View File

@ -1,70 +1,71 @@
import React, { Component } from 'react';
import {
Box,
Icon,
Row,
Text,
Button,
Col,
LoadingSpinner,
} from '@tlon/indigo-react';
import { Sigil } from './sigil.js'
import { Box, Icon, Row, Text, LoadingSpinner } from '@tlon/indigo-react';
export default class TxAction extends Component {
constructor(props) {
super(props);
}
render() {
const leftIcon =
(this.props.action === "sent") ? "ArrowSouth" :
(this.props.action === "recv") ? "ArrowNorth" :
(this.props.action === "fail") ? "X" :
"NullIcon";
this.props.action === 'sent'
? 'ArrowSouth'
: this.props.action === 'recv'
? 'ArrowNorth'
: this.props.action === 'fail'
? 'X'
: 'NullIcon';
const actionColor =
(this.props.action === "sent") ? "sentBlue" :
(this.props.action === "recv") ? "recvGreen" :
(this.props.action === "fail") ? "gray" :
"red";
this.props.action === 'sent'
? 'sentBlue'
: this.props.action === 'recv'
? 'recvGreen'
: this.props.action === 'fail'
? 'gray'
: 'red';
const actionText =
(this.props.action === "sent" && !this.props.pending) ? "Sent BTC" :
(this.props.action === "sent" && this.props.pending) ? "Sending BTC" :
(this.props.action === "recv" && !this.props.pending) ? "Received BTC" :
(this.props.action === "recv" && this.props.pending) ? "Receiving BTC" :
(this.props.action === "fail") ? "Failed" :
"error";
this.props.action === 'sent' && !this.props.pending
? 'Sent BTC'
: this.props.action === 'sent' && this.props.pending
? 'Sending BTC'
: this.props.action === 'recv' && !this.props.pending
? 'Received BTC'
: this.props.action === 'recv' && this.props.pending
? 'Receiving BTC'
: this.props.action === 'fail'
? 'Failed'
: 'error';
const pending = (!this.props.pending) ? null :
<LoadingSpinner
background="midOrange"
foreground="orange"
/>
const pending = !this.props.pending ? null : (
<LoadingSpinner background="midOrange" foreground="orange" />
);
const url = (this.props.network === 'testnet')
? `http://blockstream.info/testnet/tx/${this.props.txid}`
: `http://blockstream.info/tx/${this.props.txid}`;
const url =
this.props.network === 'testnet'
? `http://blockstream.info/testnet/tx/${this.props.txid}`
: `http://blockstream.info/tx/${this.props.txid}`;
return (
<Row alignItems="center">
<Box backgroundColor={actionColor}
width="24px"
height="24px"
textAlign="center"
alignItems="center"
borderRadius="2px"
mr={2}
p={1}
<Box
backgroundColor={actionColor}
width="24px"
height="24px"
textAlign="center"
alignItems="center"
borderRadius="2px"
mr={2}
p={1}
>
<Icon icon={leftIcon} color="white"/>
<Icon icon={leftIcon} color="white" />
</Box>
<Text color={actionColor} fontSize="14px">{actionText}</Text>
<a href={url} target="_blank">
<Icon color={actionColor} icon="ArrowNorthEast" ml={1} mr={2}/>
<Text color={actionColor} fontSize="14px">
{actionText}
</Text>
<a href={url} target="_blank" rel="noreferrer">
<Icon color={actionColor} icon="ArrowNorthEast" ml={1} mr={2} />
</a>
{pending}
</Row>

View File

@ -1,28 +1,16 @@
import React, { Component } from 'react';
import { BrowserRouter, Route } from "react-router-dom";
import _ from 'lodash';
import { BrowserRouter } from 'react-router-dom';
import { api } from '../api.js';
import { store } from '../store.js';
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
import { ThemeProvider } from 'styled-components';
import light from './themes/light';
import dark from './themes/dark';
import {
Text,
Box,
Reset,
Col,
LoadingSpinner,
} from '@tlon/indigo-react';
// import dark from './themes/dark';
import { Box, Reset } from '@tlon/indigo-react';
import StartupModal from './lib/startupModal.js';
import Header from './lib/header.js'
import Balance from './lib/balance.js'
import Transactions from './lib/transactions.js'
import Warning from './lib/warning.js'
import Body from './lib/body.js'
import { subscription } from '../subscription.js'
import Body from './lib/body.js';
import { subscription } from '../subscription.js';
const network = "bitcoin";
const network = 'bitcoin';
export class Root extends Component {
constructor(props) {
@ -32,8 +20,8 @@ export class Root extends Component {
store.setStateHandler(this.setState.bind(this));
}
componentDidMount(){
this.props.channel.setOnChannelError((e) => {
componentDidMount() {
this.props.channel.setOnChannelError(() => {
subscription.start();
});
subscription.start();
@ -42,34 +30,38 @@ export class Root extends Component {
render() {
const loaded = this.state.loaded;
const warning = this.state.showWarning;
const blur = (!loaded) ? false :
!(this.state.wallet && this.state.provider);
const blur = !loaded ? false : !(this.state.wallet && this.state.provider);
return (
<BrowserRouter>
<ThemeProvider theme={light}>
<Reset/>
{ (loaded) ? <StartupModal api={api} state={this.state} network={network}/> : null }
<Box display="flex"
flexDirection='column'
position='absolute'
alignItems='center'
backgroundColor='lightOrange'
width='100%'
<Reset />
{loaded ? (
<StartupModal api={api} state={this.state} network={network} />
) : null}
<Box
display="flex"
flexDirection="column"
position="absolute"
alignItems="center"
backgroundColor="lightOrange"
width="100%"
minHeight={loaded ? '100%' : 'none'}
height={loaded ? 'none' : '100%'}
style={{filter: (blur ? 'blur(8px)' : 'none')}}
px={[0,4]}
pb={[0,4]}
style={{ filter: blur ? 'blur(8px)' : 'none' }}
px={[0, 4]}
pb={[0, 4]}
>
<Body loaded={loaded}
<Body
loaded={loaded}
state={this.state}
api={api} network={network}
api={api}
network={network}
warning={warning}
/>
</Box>
</ThemeProvider>
</BrowserRouter>
)
);
}
}