Commit 4c8579a4 authored by Allan Juma's avatar Allan Juma

0.6.2

parent a57149cd
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
### Description
### Steps to reproduce the behavior
### Expected behavior
### Actual behavior
### Screenshot and/or logs if applicable
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -85,8 +85,8 @@ ipcMain.on('logs-ready', () => {
function createWindow() {
console.log(path.join(__dirname, 'preload.js'));
const options = {
width: 850,
height: 705,
width: 650,
height: 950,
backgroundColor: '#0f5f76',
icon: path.join(
__dirname,
......@@ -107,7 +107,7 @@ console.log(path.join(__dirname, 'preload.js'));
if (isDev) {
// Add width for dev tools
options.width += 720;
options.height += 450;
options.height += 0;
win = new BrowserWindow(options);
win.loadURL('http://localhost:3000');
// Open the DevTools.
......
......@@ -112,11 +112,11 @@ module.exports.startLndProcess = async function({
if (isDev) {
args = args.concat([
'--bitcoin.node=bitcoind',
'--bitcoind.rpchost=192.168.1.3',
'--bitcoind.rpchost=btc-p2p.bitsoko.org',
'--bitcoind.rpcuser=bitsoko',
'--bitcoind.rpcpass=12Bitcoinsrus12',
'--bitcoind.zmqpubrawblock=tcp://192.168.1.3:28332',
'--bitcoind.zmqpubrawtx=tcp://192.168.1.3:28333'
'--bitcoind.zmqpubrawblock=tcp://btc-p2p.bitsoko.org:28332',
'--bitcoind.zmqpubrawtx=tcp://btc-p2p.bitsoko.org:28333'
]);
}
......
......@@ -17,6 +17,7 @@ import LogAction from './log';
import InfoAction from './info';
import NotificationAction from './notification';
import ChannelAction from './channel';
import InvestmentAction from './investment';
import TransactionAction from './transaction';
import DaoAction from './dao';
import PoolAction from './pool';
......@@ -51,6 +52,7 @@ export const info = new InfoAction(store, grpc, nav, notify);
export const pool = new PoolAction(store, grpc, nav, notify);
export const dao = new DaoAction(store, grpc, nav, notify);
export const promotion = new PromotionAction(store, grpc, nav, notify);
export const investment = new InvestmentAction(store, grpc, nav, notify);
export const transaction = new TransactionAction(store, grpc, nav, notify);
export const channel = new ChannelAction(store, grpc, nav, notify);
export const invoice = new InvoiceAction(store, grpc, nav, notify, Clipboard);
......@@ -123,29 +125,63 @@ when(
* the user can really begin to interact with the application and calls
* to and from lnd can be done. The contract chains are implemented
*/
function setChainAccount(chain, account) {
if (!store.settings.chains[chain]) {
throw new Error(`Invalid chain: `+chain);
}
if (!account) {
throw new Error(`Invalid seed: `+account);
}
store.settings.chains[chain].account = account;
db.save();
console.log('INFO! '+chain+' account '+account.address);
}
function setChainAddress(chain, addr) {
if (!store.settings.chains[chain]) {
throw new Error(`Invalid chain: `+chain);
}
if (!addr) {
throw new Error(`Invalid address: `+addr);
}
store.settings.chains[chain].addresses.push(addr);
db.save();
}
when(
() => store.lndReady,
async () => {
console.log(store.settings.chains);
if(!store.chains.algo.account){
if(!store.settings.chains.algo.account){
var algoAccount = await Algorand.createWallet();
store.chains.algo.account = algoAccount;
//var algoAccountInfo = await Algorand.getAccount('testnet',store.chains.algo.account.address);
setChainAccount('algo', algoAccount);
console.log('new algo account! '+store.settings.chains.algo.account.address);
}else{
console.log('old algo account! '+store.settings.chains.algo.account.address);
}
console.log('INFO! algorand account '+store.chains.algo.account.address);
/*
if(!store.chains.sol.account){
var solAccount = await Solana.createWallet();
store.chains.sol.account = solAccount;
//var solAccountInfo = await Solana.getAccount('testnet',store.chains.sol.account.address);
setChainAccount({chain:'sol', account:solAccount});
}
console.log('INFO! solana account '+store.chains.sol.account.address);
*/
}
);
......
/**
* @fileOverview actions to set Investments state within the app and to
* call the corresponding apis for listing transactions.
*/
import * as log from './log';
import { parseDate, toHex } from '../helper';
class InvestmentAction {
constructor(store, grpc, nav, notification) {
this._store = store;
this._grpc = grpc;
this._nav = nav;
this._notification = notification;
}
/**
* Initiate the transaction list view by navigating to the view and updating
* the app's transaction state by calling all necessary grpc apis.
* @return {undefined}
*/
init() {
this._nav.goInvestments();
this.update();
}
/**
* Select a transaction item from the transaction list view and then navigate
* to the detail view to list transaction parameters.
* @param {Object} options.item The selected transaction object
* @return {Promise<undefined>}
*/
async select({ item }) {
this._store.selectedTransaction = item;
if (item.paymentRequest) {
item.memo = await this.decodeMemo({ payReq: item.paymentRequest });
}
this._nav.goTransactionDetail();
this.update();
}
/**
* Update the on-chain transactions, invoice, and lighting payments in the
* app state by querying all required grpc apis.
* @return {Promise<undefined>}
*/
async update() {
await Promise.all([
this.getAlgoTransactions(),
]);
}
/**
* Get lock state.
* @return {Promise<undefined>}
*/
async updateLock(TransactionTime) {
let locked=true;
if(TransactionTime < new Date().setDate(new Date().getDate() - 1)){
locked=false;
}
return locked;
}
/**
* List the on-chain transactions by calling the respective grpc api and updating
* the transactions array in the global store.
* @return {Promise<undefined>}
*/
async getAlgoTransactions() {
try {
const { transactions } = await this._grpc.sendCommand('listInvoices');
this._store.transactions = transactions.map(transaction => ({
id: transaction.txHash,
type: this.updateLock(transaction.timeStamp) ? 'locked' : 'unlocked',
amount: transaction.amount,
fee: transaction.totalFees,
confirmations: transaction.numConfirmations,
status: transaction.numConfirmations < 3 ? 'rewarded' : 'confirmed',
date: parseDate(transaction.timeStamp),
}));
} catch (err) {
log.error('Listing transactions failed', err);
}
}
/**
* Subscribe to incoming on-chain transactions using the grpc streaming api.
* @return {Promise<undefined>}
*/
async subscribeTransactions() {
const stream = this._grpc.sendStreamCommand('subscribeTransactions');
await new Promise((resolve, reject) => {
stream.on('data', () => this.update());
stream.on('end', resolve);
stream.on('error', reject);
stream.on('status', status => log.info(`Transactions update: ${status}`));
});
}
}
export default InvestmentAction;
......@@ -167,6 +167,10 @@ class NavAction {
goTransactions() {
this._store.route = 'Transactions';
}
goInvestments() {
this._store.route = 'Investments';
}
goPools() {
this._store.route = 'Pools';
......@@ -189,6 +193,12 @@ class NavAction {
this._store.route = 'Notifications';
}
goContractSettings(contract) {
this._store.selectedContractSetting = contract;
this._store.route = 'SetContract';
}
goSettings() {
this._store.route = 'Settings';
}
......
......@@ -139,7 +139,7 @@ class PoolAddAction {
const { settings } = this._store;
var amount = toSatoshis(amount, settings);
//get new pool quotation from server
var escrowInvoice = await sendToEscrow(amount, '{"state":"pool","action":"add","account":"algo1","pool":"pool1"}');
var escrowInvoice = await sendToEscrow(amount, '{"state":"pool", "action":"add", "account":"'+this._store.settings.chains.algo.account.address+'", "pool":"'+this._store.selectedPool.id+'", "type":"float"}');
console.log(escrowInvoice);
this.setAddress({ address: escrowInvoice });
......
......@@ -60,20 +60,24 @@ class PoolAction {
async getPools() {
try {
const uri = 'https://gateway.bitsoko.org/getEnterprise/?servEntID=1';
const uri =
'https://gateway.bitsoko.org/getEnterpriseRoute';
//const uri = 'https://gateway.bitsoko.org/getEnterpriseRoute';
const response = checkHttpStatus(await fetch(uri));
const pools = await response.json();
const svs = await response.json();
var pools = svs.services;
//const pools = (await response.json()).tickers;
//const rate = tickers.find(t => t.ticker.toLowerCase() === fiat).rate;
this._store.pools = pools.map(pool => ({
id: pool.url,
id: pool.id,
cat: 'micro',
type: 'algorand',
title: pool.title ? pool.title : 'Sample merchant @ https://demo1.bitsoko.org',
desc: pool.desc ? pool.desc : '{name:"demo", desc:"a sample merchant", image:"",banner:"",link:"",url:"https://demo1.bitsoko.org"}',
icon: pool.bannerPath,
banner: pool.bannerPath,
url: 'https://demo1.bitsoko.org',
title: pool.name,
desc: pool.description,
amount: 0,
fee: 0,
confirmations: 5,
......
......@@ -115,22 +115,69 @@ const countStyles = StyleSheet.create({
paddingRight: 10,
paddingLeft: 10,
borderRadius: 13,
},
red: {
backgroundColor: color.pinkSig,
},
orange: {
backgroundColor: color.orangeSig,
},
green: {
backgroundColor: color.greenSig,
},
grey: {
backgroundColor: color.greySig,
},
txt: {
fontSize: font.sizeXS,
lineHeight: font.lineHeightXS,
},
});
export const CountBubble = ({ children, style }) =>
export const CountBubbleRed = ({ children, style }) =>
children && children !== '0' ? (
<View style={[countStyles.red, countStyles.bubble, style]}>
<H4Text style={countStyles.txt}>{children}</H4Text>
</View>
) : null;
export const CountBubbleGreen = ({ children, style }) =>
children && children !== '0' ? (
<View style={[countStyles.green, countStyles.bubble, style]}>
<H4Text style={countStyles.txt}>{children}</H4Text>
</View>
) : null;
export const CountBubbleOrange = ({ children, style }) =>
children && children !== '0' ? (
<View style={[countStyles.bubble, style]}>
<View style={[countStyles.orange, countStyles.bubble, style]}>
<H4Text style={countStyles.txt}>{children}</H4Text>
</View>
) : null;
CountBubble.propTypes = {
export const CountBubbleGrey = ({ children, style }) =>
children && children !== '0' ? (
<View style={[countStyles.bubble, countStyles.grey, style]}>
<H4Text style={countStyles.txt}>{children}</H4Text>
</View>
) : null;
CountBubbleRed.propTypes = {
children: PropTypes.string.isRequired,
style: ViewPropTypes.style,
};
CountBubbleOrange.propTypes = {
children: PropTypes.string.isRequired,
style: ViewPropTypes.style,
};
CountBubbleGreen.propTypes = {
children: PropTypes.string.isRequired,
style: ViewPropTypes.style,
};
CountBubbleGrey.propTypes = {
children: PropTypes.string.isRequired,
style: ViewPropTypes.style,
};
......
......@@ -7,7 +7,7 @@ import {
} from 'react-native';
import PropTypes from 'prop-types';
import { color, font } from './style';
import LightningBoltIcon from '../asset/icon/lightning-bolt';
import BitcoinIcon from '../../src/asset/icon/bitcoin';
import Text from './text';
import Svg, { Path, Circle, Defs, Stop, LinearGradient } from './svg';
import { generateArc } from '../helper';
......@@ -53,7 +53,8 @@ export const LoadNetworkSpinner = ({ continuous, percentage, msg, style }) => (
progressWidth={3}
gradient="loadNetworkGrad"
>
<LightningBoltIcon height={28} width={14.2222} />
<BitcoinIcon height={38} width={25} />
</ResizeableSpinner>
<Text style={loadNetworkStyles.copy}>{msg}</Text>
</View>
......
......@@ -89,6 +89,23 @@ const ComputedChannel = store => {
? 'info'
: 'error';
},
get contractStatus() {
const {
channelBalanceOpenSatoshis: opened,
channelBalanceInactiveSatoshis: inactive,
channelBalancePendingSatoshis: pending,
} = store;
return 'info';
/*
return opened
? 'success'
: inactive
? 'inactive'
: pending
? 'info'
: 'error';
*/
},
});
};
......
......@@ -16,9 +16,9 @@ const ComputedPool = store => {
all.sort((a, b) => b.date.getTime() - a.date.getTime());
all.forEach((t, i) => {
t.key = String(i);
t.idName = t.type === 'algorand' ? 'Algorand' : 'Ethereum';
//t.icon = '../asset/icon/'+t.type+'.png';
t.icon = '../asset/icon/lightning.svg';
//t.idName = t.type === 'algorand' ? '209:BITS:dooca-net' : 'Ethereum';
t.idName = t.id+':BITS:dooca.net | '+t.title;
t.icon = t.icon;
t.typeLabel = toCaps(t.type);
t.statusLabel = toCaps(t.status);
t.merchantLabel = t.title;
......
......@@ -63,28 +63,6 @@ export class Store {
restoreIndex: 0,
focusedRestoreInd: 0,
},
chains: {
eth:{
account:'',
rpc:{mainnet: {server:'',port:'',key:''}, testnet: {server:'',port:'',key:''}}
},
matic:{
account:'',
rpc:{mainnet: {server:'',port:'',key:''}, testnet: {server:'',port:'',key:''}}
},
xml:{
account:'',
rpc:{mainnet: {server:'',port:'',key:''}, testnet: {server:'',port:'',key:''}}
},
sol:{
account:'',
rpc:{mainnet: {server:'',port:'',key:''}, testnet: {server:'',port:'',key:''}}
},
algo:{
account:'',
rpc:{mainnet: {server:'',port:'',key:''}, testnet: {server:'https://testnet-algorand.api.purestake.io/ps2',port:'',key:'qe3CNhIJ3C5DXrGHlfxAU1aIUt7SrYzNMwHz8gKb'}}
},
},
promotions: [],
selectedPromotion: null,
pools: [],
......@@ -127,6 +105,7 @@ export class Store {
// Persistent data
settings: {
contracts: ['algo','eth'],
unit: DEFAULT_UNIT,
fiat: DEFAULT_FIAT,
displayFiat: true,
......@@ -134,6 +113,14 @@ export class Store {
restoring: false,
autopilot: true,
nodeScores: {},
chains: {
algo:{
account:false,
chain: 'testnet',
addresses: [],
rpc:{mainnet: {server:'',port:'',key:''}, testnet: {server:'https://testnet-algorand.api.purestake.io/ps2',port:'',key:'qe3CNhIJ3C5DXrGHlfxAU1aIUt7SrYzNMwHz8gKb'}}
}
}
},
});
}
......
......@@ -74,6 +74,8 @@ const HomeView = ({
unitLabel,
channelStatus,
channelPercentageLabel,
contractStatus,
contractPercentageLabel,
} = store;
return (
<Background image="purple-gradient-bg">
......@@ -89,6 +91,8 @@ const HomeView = ({
unitLabel={unitLabel}
channelStatus={channelStatus}
channelPercentageLabel={channelPercentageLabel}
contractStatus={contractStatus}
contractPercentageLabel={contractPercentageLabel}
toggleDisplayFiat={() => wallet.toggleDisplayFiat()}
goChannels={() => channel.init()}
/>
......@@ -137,6 +141,7 @@ const balanceStyles = StyleSheet.create({
},
alert: {
marginRight: 6,
marginLeft: '50%',
},
});
......@@ -146,6 +151,8 @@ const BalanceDisplay = ({
unitLabel,
channelStatus,
channelPercentageLabel,
contractStatus,
contractPercentageLabel,
toggleDisplayFiat,
goChannels,
}) => (
......@@ -159,8 +166,17 @@ const BalanceDisplay = ({
<Button onPress={goChannels} style={balanceStyles.percentBtn}>
<View style={{flex:2,flexDirection:"column",justifyContent:"center",width:"100%",padding:10,paddingTop:50,textAlign:"center"}}>
<View style={{flex:1,padding:10,marginTop: "-10px"}}>
<Alert type={channelStatus} style={balanceStyles.alert} />
<H4Text>{channelPercentageLabel}</H4Text>
<H4Text style={{paddingTop: "5px"}}>{channelPercentageLabel}{"\n"}</H4Text>
</View>
<View style={{flex:1,padding:10,marginTop: "-10px"}}>
<Alert type={contractStatus} style={balanceStyles.alert} />
<H4Text style={{paddingTop: "5px"}}>0% on Contracts</H4Text>
</View>
</View>
</Button>
</View>
);
......@@ -170,6 +186,8 @@ BalanceDisplay.propTypes = {
unitLabel: PropTypes.string,
channelStatus: PropTypes.string.isRequired,
channelPercentageLabel: PropTypes.string.isRequired,
contractStatus: PropTypes.string.isRequired,
contractPercentageLabel: PropTypes.string.isRequired,
toggleDisplayFiat: PropTypes.func.isRequired,
goChannels: PropTypes.func.isRequired,
};
......
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';
import Background from '../component/background';
import { Header, Title } from '../component/header';
import { Button, CancelButton } from '../component/button';
import { ListContent, List, ListItem, ListHeader } from '../component/list';
import { Alert } from '../component/notification';
import Text from '../component/text';
import BitcoinIcon from '../../src/asset/icon/bitcoin';
import LightningBoltIcon from '../../src/asset/icon/lightning-bolt';
import { color, font } from '../component/style';
//
// Investment View
//
const InvestmentView = ({ store, nav, transaction }) => {
const { computedTransactions: transactions } = store;
return (
<Background color={color.blackDark}>
<Header separator>
<Button disabled onPress={() => {}} />
<Title title="Investments" />
<CancelButton onPress={() => nav.goPools()} />
</Header>
<ListContent>
<List
data={transactions}
renderHeader={InvestmentListHeader}
renderItem={item => (
<InvestmentListItem
tx={item}
onSelect={() => transaction.select({ item })}
/>
)}
/>
</ListContent>
</Background>
);
};
InvestmentView.propTypes = {
store: PropTypes.object.isRequired,
nav: PropTypes.object.isRequired,
transaction: PropTypes.object.isRequired,
};
//
// Transaction List Item
//
const iStyles = StyleSheet.create({
item: {
paddingLeft: 10,
paddingRight: 10,
},
wrap: {
paddingRight: 50,
},
txt: {
color: color.white,
fontSize: font.sizeS,
},
alert: {
marginRight: 6,
},
group: {
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
},
l: { flex: 8 },
m: { flex: 4 },
s: { flex: 2 },
i: { flex: 1 },
});
const statusType = tx => {
if (tx.type === 'locked') {
return tx.status === 'rewarded' ? 'success' : 'error';
} else {
return tx.status === 'rewarded' ? 'success' : 'info';
}
};
const InvestmentListItem = ({ tx, onSelect }) => (
<ListItem style={iStyles.item} onSelect={onSelect}>
<View style={iStyles.i}>
{(tx.type === 'unlocked' && tx.status === 'rewarded') ? (
<LightningBoltIcon height={126 * 0.14} width={64 * 0.14} />
) : (
<BitcoinIcon height={170 * 0.08} width={135 * 0.08} />
)}
</View>
<View style={[iStyles.m, iStyles.group]}>
<Alert type={statusType(tx)} style={iStyles.alert} />
<Text style={iStyles.txt}>{tx.statusLabel}</Text>
</View>
<Text style={[iStyles.m, iStyles.txt]}>{tx.dateLabel}</Text>
<View style={iStyles.l}>
<Text style={[iStyles.txt, iStyles.wrap]} numberOfLines={1}>
{tx.id}
</Text>
</View>
<Text style={[iStyles.m, iStyles.txt]}>{tx.amountLabel}</Text>
<Text style={[iStyles.s, iStyles.txt]}>{tx.feeLabel}</Text>
</ListItem>
);
InvestmentListItem.propTypes = {
tx: PropTypes.object.isRequired,
onSelect: PropTypes.func.isRequired,
};
//
// Transaction List Header
//
const hStyles = StyleSheet.create({
txt: {
color: color.greyListHeader,
fontSize: font.sizeXS,
},
header: {
backgroundColor: color.blackDark,
},
});
const InvestmentListHeader = () => (
<ListHeader style={[iStyles.item, hStyles.header]}>
<View style={iStyles.i} />
<Text style={[iStyles.m, hStyles.txt]}>STATUS</Text>
<Text style={[iStyles.m, hStyles.txt]}>DATE</Text>
<Text style={[iStyles.l, hStyles.txt]}>CONTRACT ID</Text>
<Text style={[iStyles.m, hStyles.txt]}>AMOUNT</Text>
<Text style={[iStyles.s, hStyles.txt]}>FEE</Text>
</ListHeader>
);
export default observer(InvestmentView);
......@@ -40,12 +40,14 @@ import ChannelDetail from './channel-detail';
import ChannelDelete from './channel-delete';
import ChannelCreate from './channel-create';
import PoolAdd from './pool-add';
import Investment from './investment';
import Transaction from './transaction';
import Promotion from './promotion';
import Pool from './pool';
import Setting from './setting';
import SettingUnit from './setting-unit';
import SettingFiat from './setting-fiat';
import SetContract from './set-contract';
</