Merge pull request #2 from status-im/watermelon-data-persist
Add data persistence and syncing
This commit is contained in:
commit
30736c7a9b
|
@ -0,0 +1,67 @@
|
|||
import { Q } from '@nozbe/watermelondb'
|
||||
import database from '../db'
|
||||
import { getAllLPEvents, GIVER_ADDED, DELEGATE_ADDED, PROJECT_ADDED } from '../utils/events'
|
||||
|
||||
const lpCollection = database.collections.get('lp_events')
|
||||
export const addEvent = async data => {
|
||||
await database.action(async () => {
|
||||
const res = await lpCollection.create(lpEvent => {
|
||||
const { event, address, id, blockNumber } = data
|
||||
lpEvent.eventId = id
|
||||
lpEvent.address = address
|
||||
lpEvent.event = event
|
||||
lpEvent.blockNumber = blockNumber
|
||||
})
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
export const batchAddEvents = async events => {
|
||||
const batch = events.map(e => {
|
||||
return lpCollection.prepareCreate(lpEvent => {
|
||||
const { event, address, id, blockNumber, returnValues } = e
|
||||
lpEvent.eventId = id
|
||||
lpEvent.address = address
|
||||
lpEvent.event = event
|
||||
lpEvent.blockNumber = blockNumber
|
||||
lpEvent.returnValues = returnValues
|
||||
})
|
||||
})
|
||||
return await database.action(async () => await database.batch(...batch))
|
||||
}
|
||||
|
||||
export const getLatestProfileEvents = async eventIds => {
|
||||
const events = await lpCollection.query(
|
||||
Q.where(
|
||||
'id',
|
||||
Q.notIn(eventIds)
|
||||
),
|
||||
Q.where(
|
||||
'event',
|
||||
Q.oneOf([GIVER_ADDED, DELEGATE_ADDED, PROJECT_ADDED])
|
||||
)
|
||||
).fetch()
|
||||
return events
|
||||
}
|
||||
|
||||
|
||||
export const getLpEventById = async id => {
|
||||
const event = await lpCollection.query(
|
||||
Q.where('event_id', id)
|
||||
).fetch()
|
||||
return event
|
||||
}
|
||||
|
||||
export const getLastBlockStored = async () => {
|
||||
const col = await lpCollection.query().fetch()
|
||||
const blockNumber = col.length
|
||||
? col.sort((a,b) => b.blockNumber - a.blockNumber)[0].blockNumber
|
||||
: 0
|
||||
return blockNumber
|
||||
}
|
||||
|
||||
export const getAndAddLpEvents = async () => {
|
||||
const lastBlock = await getLastBlockStored()
|
||||
const events = await getAllLPEvents(lastBlock + 1)
|
||||
batchAddEvents(events)
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import { Q } from '@nozbe/watermelondb'
|
||||
import database from '../db'
|
||||
import { getAllPledges } from '../utils/pledges'
|
||||
import { getProfilesById } from './profiles'
|
||||
|
||||
const createPledge = (pledge, data, profiles) => {
|
||||
const { id, owner, amount, token, commitTime, nDelegates, pledgeState, intendedProject } = data
|
||||
const profile = profiles.find(p => p.idProfile == owner)
|
||||
pledge.pledgeId = Number(id)
|
||||
pledge.owner = Number(owner)
|
||||
pledge.amount = amount
|
||||
pledge.token = token
|
||||
pledge.commitTime = Number(commitTime)
|
||||
pledge.nDelegates = Number(nDelegates)
|
||||
pledge.pledgeState = pledgeState
|
||||
pledge.intendedProject = Number(intendedProject)
|
||||
pledge.profile.set(profile)
|
||||
}
|
||||
|
||||
const pledgesCollection = database.collections.get('pledges')
|
||||
export const addPledge = async data => {
|
||||
return await database.action(async () => {
|
||||
const res = await pledgesCollection.create(pledge => createPledge(pledge, data))
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
export const batchAddPledges = async (pledges, profiles = []) => {
|
||||
const batch = pledges.map(data => {
|
||||
return pledgesCollection.prepareCreate(pledge => createPledge(pledge, data, profiles))
|
||||
})
|
||||
console.log({batch})
|
||||
return await database.action(async () => await database.batch(...batch))
|
||||
}
|
||||
|
||||
const getLastPledge = pledges => {
|
||||
const pledgeId = pledges.length
|
||||
? pledges.sort((a,b) => b.pledgeId - a.pledgeId)[0].pledgeId
|
||||
: 0
|
||||
return pledgeId
|
||||
}
|
||||
export const getAndAddPledges = async () => {
|
||||
const pledges = await getLocalPledges()
|
||||
const pledgeId = getLastPledge(pledges)
|
||||
const newPledges = await getAllPledges(pledgeId + 1)
|
||||
const pledgeIds = newPledges.map(p => p.owner)
|
||||
const profiles = await getProfilesById(pledgeIds)
|
||||
batchAddPledges(newPledges, profiles)
|
||||
}
|
||||
|
||||
export const getLocalPledges = async () => {
|
||||
const events = await pledgesCollection.query().fetch()
|
||||
return events
|
||||
}
|
||||
|
||||
export const getPledgeById = async id => {
|
||||
const event = await pledgesCollection.query(
|
||||
Q.where('id_profile', id)
|
||||
).fetch()
|
||||
return event
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import { Q } from '@nozbe/watermelondb'
|
||||
import database from '../db'
|
||||
import { getLatestProfileEvents } from './lpEvents'
|
||||
import { formatFundProfileEvent } from '../utils/events'
|
||||
|
||||
const profilesCollection = database.collections.get('profiles')
|
||||
export const addProfile = async data => {
|
||||
return await database.action(async () => {
|
||||
const res = await profilesCollection.create(profile => {
|
||||
const { id, addr, canceled, commitTime, type, name, url, idProfile } = data
|
||||
profile.eventId = id
|
||||
profile.addr = addr
|
||||
profile.canceled = canceled
|
||||
profile.commitTime = Number(commitTime)
|
||||
profile.type = type
|
||||
profile.name = name
|
||||
profile.url = url
|
||||
profile.idProfile = Number(idProfile)
|
||||
})
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
export const batchAddProfiles = async profiles => {
|
||||
const batch = profiles.map(data => {
|
||||
return profilesCollection.prepareCreate(profile => {
|
||||
const { id, addr, canceled, commitTime, type, name, url, idProfile } = data
|
||||
profile.eventId = id
|
||||
profile.addr = addr
|
||||
profile.canceled = canceled
|
||||
profile.commitTime = Number(commitTime)
|
||||
profile.type = type
|
||||
profile.name = name
|
||||
profile.url = url
|
||||
profile.idProfile = Number(idProfile)
|
||||
})
|
||||
})
|
||||
return await database.action(async () => await database.batch(...batch))
|
||||
}
|
||||
|
||||
export const addFormattedProfiles = async () => {
|
||||
const allProfiles = await getAllProfiles()
|
||||
const allEventIds = allProfiles.map(p => p.eventId)
|
||||
const events = await getLatestProfileEvents(allEventIds)
|
||||
const formattedEvents = await Promise.all(
|
||||
events.map(formatFundProfileEvent)
|
||||
)
|
||||
await batchAddProfiles(formattedEvents)
|
||||
}
|
||||
|
||||
export const getAllProfiles = async () => {
|
||||
const events = await profilesCollection.query().fetch()
|
||||
return events
|
||||
}
|
||||
|
||||
export const getProfileById = async id => {
|
||||
const event = await profilesCollection.query(
|
||||
Q.where('id_profile', id)
|
||||
).fetch()
|
||||
return event
|
||||
}
|
||||
|
||||
export const getProfilesById = async ids => {
|
||||
const event = await profilesCollection.query(
|
||||
Q.where(
|
||||
'id_profile',
|
||||
Q.oneOf(ids)
|
||||
)
|
||||
).fetch()
|
||||
return event
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import { Q } from '@nozbe/watermelondb'
|
||||
import database from '../db'
|
||||
import { ALL_EVENTS, getAllVaultEvents } from '../utils/events'
|
||||
|
||||
const vaultCollection = database.collections.get('vault_events')
|
||||
export const addEvent = async data => {
|
||||
await database.action(async () => {
|
||||
const res = await vaultCollection.create(lpEvent => {
|
||||
const { event, address, id, blockNumber } = data
|
||||
lpEvent.eventId = id
|
||||
lpEvent.address = address
|
||||
lpEvent.event = event
|
||||
lpEvent.blockNumber = blockNumber
|
||||
})
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
export const batchAddEvents = async events => {
|
||||
const batch = events.map(e => {
|
||||
return vaultCollection.prepareCreate(lpEvent => {
|
||||
const { event, address, id, blockNumber, returnValues } = e
|
||||
lpEvent.eventId = id
|
||||
lpEvent.address = address
|
||||
lpEvent.event = event
|
||||
lpEvent.blockNumber = blockNumber
|
||||
lpEvent.returnValues = returnValues
|
||||
})
|
||||
})
|
||||
return await database.action(async () => await database.batch(...batch))
|
||||
}
|
||||
|
||||
export const getVaultEventById = async id => {
|
||||
const event = await vaultCollection.query(
|
||||
Q.where('event_id', id)
|
||||
).fetch()
|
||||
return event
|
||||
}
|
||||
|
||||
export const getLastBlockStored = async () => {
|
||||
const col = await vaultCollection.query().fetch()
|
||||
const blockNumber = col.length
|
||||
? col.sort((a,b) => b.blockNumber - a.blockNumber)[0].blockNumber
|
||||
: 0
|
||||
return blockNumber
|
||||
}
|
||||
|
||||
export const getAndAddVaultEvents = async () => {
|
||||
const lastBlock = await getLastBlockStored()
|
||||
const events = await getAllVaultEvents(lastBlock + 1)
|
||||
batchAddEvents(events)
|
||||
}
|
|
@ -1,12 +1,22 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { withDatabase } from '@nozbe/watermelondb/DatabaseProvider'
|
||||
import withObservables from '@nozbe/with-observables'
|
||||
import PledgeAllocationsChart from './dashboard/PledgeAllocationsChart'
|
||||
import FundingSummary from './dashboard/FundingSummary'
|
||||
|
||||
const Dashboard = () => (
|
||||
const Dashboard = ({ pledges }) => (
|
||||
<div>
|
||||
<FundingSummary title="Funding Summary" />
|
||||
<PledgeAllocationsChart title="Pledge Allocations" />
|
||||
<FundingSummary title="Funding Summary" pledges={pledges} />
|
||||
<PledgeAllocationsChart title="Pledge Allocations" pledges={pledges} />
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Dashboard
|
||||
Dashboard.propTypes = {
|
||||
pledges: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
export default withDatabase(withObservables([], ({ database }) => ({
|
||||
pledges: database.collections.get('pledges').query().observe()
|
||||
}))(Dashboard))
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React, { Fragment, memo } from 'react'
|
||||
import React, { Fragment } from 'react'
|
||||
import MaterialTable from 'material-table'
|
||||
import LiquidPledging from 'Embark/contracts/LiquidPledging'
|
||||
import withObservables from '@nozbe/with-observables'
|
||||
import { withDatabase } from '@nozbe/watermelondb/DatabaseProvider'
|
||||
import { FundingContext } from '../context'
|
||||
|
||||
const { cancelProject } = LiquidPledging.methods
|
||||
|
@ -8,11 +10,11 @@ const { cancelProject } = LiquidPledging.methods
|
|||
const convertToHours = seconds => seconds / 60 / 60
|
||||
const cancelText = canceled => canceled ? 'Yes' : 'No'
|
||||
const formatField = field => ({
|
||||
...field,
|
||||
...field.getFields(),
|
||||
commitTime: convertToHours(field.commitTime),
|
||||
canceled: cancelText(field.canceled)
|
||||
})
|
||||
const FunderProfilesTable = ({ data, cancelFundProfile }) => (
|
||||
const FunderProfilesTable = ({ profiles }) => (
|
||||
<FundingContext.Consumer>
|
||||
{({ account }) =>
|
||||
<Fragment>
|
||||
|
@ -26,20 +28,21 @@ const FunderProfilesTable = ({ data, cancelFundProfile }) => (
|
|||
{ title: 'Type', field: 'type' },
|
||||
{ title: 'Canceled', field: 'canceled' }
|
||||
]}
|
||||
data={data.map(formatField)}
|
||||
data={profiles.map(formatField)}
|
||||
title="Funding Profiles"
|
||||
options={{ showEmptyDataSourceMessage: true }}
|
||||
actions={[
|
||||
rowData => ({
|
||||
icon: 'cancel',
|
||||
disabled: rowData.addr.toLowerCase() != account.toLowerCase(),
|
||||
disabled: !account || rowData.addr.toLowerCase() != account.toLowerCase(),
|
||||
tooltip: 'Cancel',
|
||||
onClick: (event, rowData) => {
|
||||
cancelProject(rowData.idProject || rowData.idProfile)
|
||||
.send()
|
||||
.then(res => {
|
||||
.then(async res => {
|
||||
console.log({res})
|
||||
cancelFundProfile(rowData.idProfile)
|
||||
const profile = profiles.find(p => p.idProfile == rowData.idProfile)
|
||||
await profile.markAsCanceled()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -50,4 +53,6 @@ const FunderProfilesTable = ({ data, cancelFundProfile }) => (
|
|||
</FundingContext.Consumer>
|
||||
)
|
||||
|
||||
export default memo(FunderProfilesTable)
|
||||
export default withDatabase(withObservables([], ({ database }) => ({
|
||||
profiles: database.collections.get('profiles').query().observeWithColumns(['canceled']),
|
||||
}))(FunderProfilesTable))
|
||||
|
|
|
@ -7,13 +7,15 @@ import AddFunder from './AddFunder'
|
|||
import CreateFunding from './CreateFunding'
|
||||
|
||||
const FundsManagement = ({ open }) => {
|
||||
const maxWidth = open ? `${window.visualViewport.width - 35}px` : '100vw'
|
||||
const windowWidth = window.visualViewport.width
|
||||
const maxWidth = open ? `${windowWidth * 0.80}px` : '100vw'
|
||||
const WebkitTransition = 'all 0.25s ease-out 0s'
|
||||
return (
|
||||
<FundingContext.Consumer>
|
||||
{({ allPledges, appendPledges, appendFundProfile, transferPledgeAmounts, fundProfiles, cancelFundProfile }) =>
|
||||
<div style={{ maxWidth }}>
|
||||
<PledgesTable data={allPledges} transferPledgeAmounts={transferPledgeAmounts} fundProfiles={fundProfiles} />
|
||||
<FunderProfilesTable data={fundProfiles} cancelFundProfile={cancelFundProfile}/>
|
||||
{({ appendPledges, appendFundProfile }) =>
|
||||
<div style={{ maxWidth, WebkitTransition }}>
|
||||
<PledgesTable />
|
||||
<FunderProfilesTable />
|
||||
<AddFunder appendFundProfile={appendFundProfile} />
|
||||
<Divider variant="middle" />
|
||||
<CreateFunding refreshTable={appendPledges} />
|
||||
|
|
|
@ -47,9 +47,6 @@ const styles = theme => ({
|
|||
appBarBg: {
|
||||
backgroundColor: '#111735'
|
||||
},
|
||||
childrenShift: {
|
||||
width: `calc(100% - ${drawerWidth}px)`
|
||||
},
|
||||
menuButton: {
|
||||
marginLeft: 12,
|
||||
marginRight: 20,
|
||||
|
@ -195,9 +192,7 @@ class PersistentDrawerLeft extends React.Component {
|
|||
})}
|
||||
>
|
||||
<div className={classes.drawerHeader} />
|
||||
<div className={classNames(classes.appBar, {
|
||||
[classes.childrenShift]: open,
|
||||
})}>
|
||||
<div className={classNames(classes.appBar)}>
|
||||
<Switch>
|
||||
<Route path="/(|dashboard)" component={Dashboard} />
|
||||
<Route path="/admin" component={ContractAdmin} />
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, { Fragment, PureComponent } from 'react'
|
||||
import React, { Fragment, Component } from 'react'
|
||||
import MaterialTable from 'material-table'
|
||||
import withObservables from '@nozbe/with-observables'
|
||||
import { withDatabase } from '@nozbe/watermelondb/DatabaseProvider'
|
||||
import { toEther } from '../utils/conversions'
|
||||
import { getTokenLabel } from '../utils/currencies'
|
||||
import TransferDialog from './TransferDialog'
|
||||
|
@ -12,50 +14,75 @@ const pledgeStateMap = {
|
|||
1: 'Paying',
|
||||
2: 'Paid'
|
||||
}
|
||||
const convertToDatetime = (field, fundProfiles) => {
|
||||
const { commitTime, owner } = field
|
||||
const profile = fundProfiles[Number(owner) - 1]
|
||||
const convertToDatetime = async field => {
|
||||
const { commitTime } = field
|
||||
const profile = await field.profile.fetch()
|
||||
if (!profile || Number(commitTime) === 0) return 0
|
||||
const time = Number(commitTime) + Number(profile.commitTime)
|
||||
const date = new Date(time * 1000)
|
||||
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
||||
}
|
||||
const formatField = (field, fundProfiles) => ({
|
||||
...field,
|
||||
commitTime: convertToDatetime(field, fundProfiles),
|
||||
const formatField = async field => ({
|
||||
...field.getFields(),
|
||||
commitTime: await convertToDatetime(field),
|
||||
amount: toEther(field.amount),
|
||||
token: getTokenLabel(field.token),
|
||||
intendedProject: projectText(field.intendedProject),
|
||||
pledgeState: pledgeStateMap[field.pledgeState]
|
||||
pledgeState: pledgeStateMap[field.pledgeState],
|
||||
transferTo: field.transferTo,
|
||||
pledge: field
|
||||
})
|
||||
class PledgesTable extends PureComponent {
|
||||
class PledgesTable extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
row: false,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setData()
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { pledges } = this.props
|
||||
const { data } = this.state
|
||||
if (data.length) {
|
||||
pledges.some((pledge, idx) => {
|
||||
const current = data[idx]
|
||||
if (current) {
|
||||
if (toEther(pledge.amount) != current.amount || pledgeStateMap[pledge.pledgeState] != current.pledgeState) this.setData()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (pledges.length && !data.length) this.setData()
|
||||
}
|
||||
|
||||
setData = async () => {
|
||||
const { pledges } = this.props
|
||||
const data = await Promise.all(pledges.map(formatField))
|
||||
this.setState({ data })
|
||||
}
|
||||
|
||||
handleClickOpen = row => {
|
||||
this.setState({ row });
|
||||
this.setState({ row })
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ row: false });
|
||||
this.setState({ row: false })
|
||||
}
|
||||
|
||||
clearRowData = () => this.setState({ rowData: null })
|
||||
|
||||
render() {
|
||||
const { data, transferPledgeAmounts, fundProfiles } = this.props
|
||||
const { row, rowData } = this.state
|
||||
const { data, row, rowData } = this.state
|
||||
return (
|
||||
<Fragment>
|
||||
<TransferDialog
|
||||
row={row}
|
||||
handleClose={this.handleClose}
|
||||
transferPledgeAmounts={transferPledgeAmounts}
|
||||
/>
|
||||
<MaterialTable
|
||||
columns={[
|
||||
{ title: 'Pledge Id', field: 'id', type: 'numeric' },
|
||||
{ title: 'Pledge Id', field: 'pledgeId', type: 'numeric' },
|
||||
{ title: 'Owner', field: 'owner' },
|
||||
{ title: 'Amount Funded', field: 'amount', type: 'numeric' },
|
||||
{ title: 'Token', field: 'token' },
|
||||
|
@ -64,7 +91,7 @@ class PledgesTable extends PureComponent {
|
|||
{ title: 'Intended Project', field: 'intendedProject' },
|
||||
{ title: 'Pledge State', field: 'pledgeState' },
|
||||
]}
|
||||
data={data.map((f) => formatField(f, fundProfiles))}
|
||||
data={data}
|
||||
title="Pledges"
|
||||
options={{ showEmptyDataSourceMessage: true }}
|
||||
actions={[
|
||||
|
@ -91,4 +118,6 @@ class PledgesTable extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
export default PledgesTable
|
||||
export default withDatabase(withObservables([], ({ database }) => ({
|
||||
pledges: database.collections.get('pledges').query().observeWithColumns(['amount', 'pledge_state']),
|
||||
}))(PledgesTable))
|
||||
|
|
|
@ -10,34 +10,42 @@ import DialogContentText from '@material-ui/core/DialogContentText'
|
|||
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||
import { getTokenLabel } from '../utils/currencies'
|
||||
import { toWei } from '../utils/conversions'
|
||||
import { FundingContext } from '../context'
|
||||
|
||||
const { transfer } = LiquidPledging.methods
|
||||
|
||||
const TransferDialog = ({ row, handleClose, transferPledgeAmounts }) => (
|
||||
const TransferDialog = ({ row, handleClose }) => (
|
||||
<Formik
|
||||
initialValues={{}}
|
||||
onSubmit={async (values, { setSubmitting, resetForm, setStatus }) => {
|
||||
const { id } = row
|
||||
const { idSender, amount, idReceiver } = values
|
||||
const args = [idSender, id, toWei(amount.toString()), idReceiver]
|
||||
const toSend = transfer(...args);
|
||||
const estimatedGas = await toSend.estimateGas();
|
||||
onSubmit={async (values, { setSubmitting, resetForm, setStatus }) => {
|
||||
const { pledgeId, pledge } = row
|
||||
const { idSender, amount, idReceiver } = values
|
||||
const args = [idSender, pledgeId, toWei(amount.toString()), idReceiver]
|
||||
const toSend = transfer(...args)
|
||||
const estimatedGas = await toSend.estimateGas()
|
||||
|
||||
toSend.send({gas: estimatedGas + 1000})
|
||||
.then(res => {
|
||||
console.log({res})
|
||||
const { events: { Transfer: { returnValues } } } = res
|
||||
transferPledgeAmounts(returnValues)
|
||||
})
|
||||
.catch(e => {
|
||||
console.log({e})
|
||||
})
|
||||
.finally(() => {
|
||||
handleClose()
|
||||
resetForm()
|
||||
})
|
||||
}}
|
||||
toSend
|
||||
.send({gas: estimatedGas + 1000})
|
||||
.then(async res => {
|
||||
console.log({res})
|
||||
const { events: { Transfer } } = res
|
||||
if (Array.isArray(Transfer)) {
|
||||
Transfer.forEach(async t => {
|
||||
const { to, amount } = t.returnValues
|
||||
await pledge.transferTo(to, amount)
|
||||
})
|
||||
} else {
|
||||
const { to, amount } = Transfer.returnValues
|
||||
await pledge.transferTo(to, amount)
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log({e})
|
||||
})
|
||||
.finally(() => {
|
||||
handleClose()
|
||||
resetForm()
|
||||
})
|
||||
}}
|
||||
>
|
||||
{({
|
||||
values,
|
||||
|
@ -60,7 +68,7 @@ const TransferDialog = ({ row, handleClose, transferPledgeAmounts }) => (
|
|||
<DialogTitle id="form-dialog-title">Transfer Funds</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{`Transfer ${values.amount || ''} ${values.amount ? getTokenLabel(row[6]) : ''} from Pledge ${row.id} ${values.idReceiver ? 'to Giver/Delegate/Project' : ''} ${values.idReceiver || ''}`}
|
||||
{`Transfer ${values.amount || ''} ${values.amount ? getTokenLabel(row[6]) : ''} from Pledge ${row.pledgeId} ${values.idReceiver ? 'to Giver/Delegate/Project' : ''} ${values.idReceiver || ''}`}
|
||||
</DialogContentText>
|
||||
<TextField
|
||||
autoFocus
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import Cytoscape from 'cytoscape'
|
||||
import dagre from 'cytoscape-dagre'
|
||||
import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import CytoscapeComponent from 'react-cytoscapejs'
|
||||
import withObservables from '@nozbe/with-observables'
|
||||
import { Q } from '@nozbe/watermelondb'
|
||||
import { withDatabase } from '@nozbe/watermelondb/DatabaseProvider'
|
||||
import { uniq, isNil } from 'ramda'
|
||||
import { toEther } from '../utils/conversions'
|
||||
import { getTokenLabel } from '../utils/currencies'
|
||||
import { FundingContext } from '../context'
|
||||
import { getAuthorizations } from '../selectors/vault'
|
||||
|
||||
|
||||
|
@ -71,21 +74,27 @@ const createElements = (transfers, vaultEvents) => {
|
|||
]
|
||||
}
|
||||
|
||||
const TransfersGraph = () => {
|
||||
const TransfersGraph = ({ transfers, vaultEvents }) => {
|
||||
return (
|
||||
<FundingContext.Consumer>
|
||||
{({ transfers, vaultEvents }) =>
|
||||
<Fragment>
|
||||
<CytoscapeComponent
|
||||
elements={createElements(transfers, vaultEvents)}
|
||||
style={ { width: '800px', height: '100%', fontSize: '14px' } }
|
||||
stylesheet={stylesheet}
|
||||
layout={layout}
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
</FundingContext.Consumer>
|
||||
<Fragment>
|
||||
<CytoscapeComponent
|
||||
elements={createElements(transfers, vaultEvents)}
|
||||
style={ { width: '100vw', height: '100%', fontSize: '14px' } }
|
||||
stylesheet={stylesheet}
|
||||
layout={layout}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default TransfersGraph
|
||||
TransfersGraph.propTypes = {
|
||||
transfers: PropTypes.array.isRequired,
|
||||
vaultEvents: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
export default withDatabase(withObservables([], ({ database }) => ({
|
||||
transfers: database.collections.get('lp_events').query(
|
||||
Q.where('event', 'Transfer')
|
||||
).observe(),
|
||||
vaultEvents : database.collections.get('vault_events').query().observe()
|
||||
}))(TransfersGraph))
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import withObservables from '@nozbe/with-observables'
|
||||
import { Q } from '@nozbe/watermelondb'
|
||||
import { withDatabase } from '@nozbe/watermelondb/DatabaseProvider'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import CardContent from '@material-ui/core/CardContent'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import LinearProgress from '@material-ui/core/LinearProgress'
|
||||
import { FundingContext } from '../../context'
|
||||
import { getDepositWithdrawTotals, getPledgesWaitingCommit } from '../../selectors/pledging'
|
||||
import { getTokenAddress } from '../../utils/currencies'
|
||||
|
||||
|
@ -47,77 +49,83 @@ const styles = {
|
|||
const getNet = (deposits, withdraws) => Number(deposits) - Number(withdraws)
|
||||
const getValue = (deposits, withdraws) => (getNet(deposits, withdraws) / Number(deposits)) * 100
|
||||
function SimpleCard(props) {
|
||||
const { classes, title } = props
|
||||
const { classes, title, transfers, pledges, vaultEvents } = props
|
||||
|
||||
return (
|
||||
<FundingContext.Consumer>
|
||||
{({ allPledges, allLpEvents, vaultEvents }) =>
|
||||
<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h5" className={classes.cardTitle}>
|
||||
{title}
|
||||
</Typography>
|
||||
{!!allLpEvents &&
|
||||
Object.entries(getDepositWithdrawTotals({ allLpEvents, allPledges, vaultEvents }))
|
||||
.map(token => {
|
||||
const [name, amounts] = token
|
||||
const { deposits, withdraws } = amounts
|
||||
const address = getTokenAddress(name)
|
||||
const pledgesForCommit = getPledgesWaitingCommit({ allPledges }).filter(p => p.token == address)
|
||||
return (
|
||||
<Card key={name}>
|
||||
<Typography variant="h5" className={classes.titleText}>
|
||||
{name}
|
||||
</Typography>
|
||||
<CardContent className={classes.fundingSummaries}>
|
||||
<Typography variant="h3">
|
||||
{Number(deposits) - Number(withdraws || 0)}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'total'} className={classes.pos} color="textSecondary">
|
||||
Remaining In Pledges
|
||||
</Typography>
|
||||
<Typography variant="h3" >
|
||||
{deposits}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'withdraw'} className={classes.pos} color="textSecondary">
|
||||
Funded
|
||||
</Typography>
|
||||
<Typography variant="h3">
|
||||
{withdraws || 0}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'deposit'} className={classes.pos} color="textSecondary">
|
||||
Withdrawn
|
||||
</Typography>
|
||||
<Typography variant="h3">
|
||||
{pledgesForCommit.length}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'deposit'} className={classes.pos} color="textSecondary">
|
||||
Pledges that can be vetoed / approved
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<LinearProgress
|
||||
classes={{
|
||||
colorPrimary: classes.linearColorPrimary,
|
||||
barColorPrimary: classes.linearBarColorPrimary,
|
||||
}}
|
||||
color="primary"
|
||||
variant="buffer"
|
||||
value={getValue(deposits, withdraws)}
|
||||
valueBuffer={100}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</CardContent>
|
||||
</Card>
|
||||
}
|
||||
</FundingContext.Consumer>
|
||||
<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h5" className={classes.cardTitle}>
|
||||
{title}
|
||||
</Typography>
|
||||
{!!transfers && !!pledges.length &&
|
||||
Object.entries(getDepositWithdrawTotals({ transfers, pledges, vaultEvents }))
|
||||
.map(token => {
|
||||
const [name, amounts] = token
|
||||
const { deposits, withdraws } = amounts
|
||||
const address = getTokenAddress(name)
|
||||
const pledgesForCommit = getPledgesWaitingCommit({ pledges }).filter(p => p.token == address)
|
||||
return (
|
||||
<Card key={name}>
|
||||
<Typography variant="h5" className={classes.titleText}>
|
||||
{name}
|
||||
</Typography>
|
||||
<CardContent className={classes.fundingSummaries}>
|
||||
<Typography variant="h3">
|
||||
{Number(deposits) - Number(withdraws || 0)}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'total'} className={classes.pos} color="textSecondary">
|
||||
Remaining In Pledges
|
||||
</Typography>
|
||||
<Typography variant="h3" >
|
||||
{deposits}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'withdraw'} className={classes.pos} color="textSecondary">
|
||||
Funded
|
||||
</Typography>
|
||||
<Typography variant="h3">
|
||||
{withdraws || 0}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'deposit'} className={classes.pos} color="textSecondary">
|
||||
Withdrawn
|
||||
</Typography>
|
||||
<Typography variant="h3">
|
||||
{pledgesForCommit.length}
|
||||
</Typography>
|
||||
<Typography variant="h6" key={name + 'veto/approve'} className={classes.pos} color="textSecondary">
|
||||
Pledges that can be vetoed / approved
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<LinearProgress
|
||||
classes={{
|
||||
colorPrimary: classes.linearColorPrimary,
|
||||
barColorPrimary: classes.linearBarColorPrimary,
|
||||
}}
|
||||
color="primary"
|
||||
variant="buffer"
|
||||
value={getValue(deposits, withdraws)}
|
||||
valueBuffer={100}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
SimpleCard.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
title: PropTypes.string
|
||||
title: PropTypes.string,
|
||||
pledges: PropTypes.array.isRequired,
|
||||
transfers: PropTypes.array.isRequired,
|
||||
vaultEvents: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
export default withStyles(styles)(SimpleCard)
|
||||
const styledCard = withStyles(styles)(SimpleCard)
|
||||
export default withDatabase(withObservables([], ({ database }) => ({
|
||||
transfers: database.collections.get('lp_events').query(
|
||||
Q.where('event', 'Transfer')
|
||||
).observe(),
|
||||
vaultEvents : database.collections.get('vault_events').query().observe()
|
||||
}))(styledCard))
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import Card from '@material-ui/core/Card'
|
|||
import CardContent from '@material-ui/core/CardContent'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import { Doughnut } from 'react-chartjs-2'
|
||||
import { FundingContext } from '../../context'
|
||||
import { toEther } from '../../utils/conversions'
|
||||
import { getTokenLabel } from '../../utils/currencies'
|
||||
import { getColor } from '../../utils/colorSchemes'
|
||||
|
@ -32,11 +31,11 @@ const pledgesChartData = pledges => {
|
|||
const labels = []
|
||||
const backgroundColor = []
|
||||
pledges.forEach((pledge, idx) => {
|
||||
const { id, amount, token } = pledge
|
||||
const { pledgeId, amount, token } = pledge
|
||||
const converted = toEther(amount)
|
||||
data.push(converted)
|
||||
labels.push(
|
||||
`pledge ${id} - ${getTokenLabel(token)}`
|
||||
`pledge ${pledgeId} - ${getTokenLabel(token)}`
|
||||
)
|
||||
backgroundColor.push(getColor('Dark2-8', idx))
|
||||
})
|
||||
|
@ -53,30 +52,27 @@ const pledgesChartData = pledges => {
|
|||
}
|
||||
|
||||
function SimpleCard(props) {
|
||||
const { classes, title } = props
|
||||
const { classes, title, pledges } = props
|
||||
|
||||
return (
|
||||
<FundingContext.Consumer>
|
||||
{({ allPledges }) =>
|
||||
<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h5" component="h2">
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography className={classes.pos} color="textSecondary">
|
||||
How your funds are distributed among pledges
|
||||
</Typography>
|
||||
<Doughnut data={pledgesChartData(allPledges)} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
}
|
||||
</FundingContext.Consumer>
|
||||
<Card className={classes.card}>
|
||||
<CardContent>
|
||||
<Typography variant="h5" component="h2">
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography className={classes.pos} color="textSecondary">
|
||||
How your funds are distributed among pledges
|
||||
</Typography>
|
||||
<Doughnut data={pledgesChartData(pledges)} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
SimpleCard.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
title: PropTypes.string
|
||||
title: PropTypes.string,
|
||||
pledges: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
export default withStyles(styles)(SimpleCard)
|
||||
|
|
|
@ -65,7 +65,7 @@ class Withdraw extends PureComponent {
|
|||
initialValues={{}}
|
||||
onSubmit={async (values, { setSubmitting, resetForm, setStatus }) => {
|
||||
const { amount } = values
|
||||
const paymentId = isPaying ? authorizedPayments.find(r => r.ref === rowData.id)['idPayment'] : rowData.id
|
||||
const paymentId = isPaying ? authorizedPayments.find(r => r.ref === rowData.id)['idPayment'] : rowData.pledgeId
|
||||
const args = isPaying ? [paymentId] : [paymentId, toWei(amount)]
|
||||
const sendFn = isPaying ? confirmPayment : withdraw
|
||||
try {
|
||||
|
@ -102,7 +102,7 @@ class Withdraw extends PureComponent {
|
|||
<Card className={classes.card} elevation={0}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" component="h2">
|
||||
{`${isPaying ? 'Confirm' : ''} Withdraw${isPaying ? 'al' : ''} ${values.amount || ''} ${values.amount ? getTokenLabel(rowData[6]) : ''} from Pledge ${rowData.id}`}
|
||||
{`${isPaying ? 'Confirm' : ''} Withdraw${isPaying ? 'al' : ''} ${values.amount || ''} ${values.amount ? getTokenLabel(rowData[6]) : ''} from Pledge ${rowData.pledgeId}`}
|
||||
</Typography>
|
||||
{!isPaying && <TextField
|
||||
className={classes.amount}
|
||||
|
|
70
app/dapp.js
70
app/dapp.js
|
@ -1,16 +1,16 @@
|
|||
import React from 'react'
|
||||
import { HashRouter as Router, Route, Link, Switch } from 'react-router-dom'
|
||||
import EmbarkJS from 'Embark/EmbarkJS';
|
||||
import LPVault from 'Embark/contracts/LPVault';
|
||||
import { HashRouter as Router } from 'react-router-dom'
|
||||
import EmbarkJS from 'Embark/EmbarkJS'
|
||||
import LiquidPledging from 'Embark/contracts/LiquidPledging'
|
||||
import web3 from 'Embark/web3'
|
||||
import { initVaultAndLP, vaultPledgingNeedsInit, standardTokenApproval, getLpAllowance } from './utils/initialize'
|
||||
import { getAllLPEvents, getAllVaultEvents, getProfileEvents, formatFundProfileEvent, getAuthorizedPayments } from './utils/events'
|
||||
import { getAllPledges, appendToExistingPledges, transferBetweenPledges } from './utils/pledges'
|
||||
import { getAuthorizedPayments } from './utils/events'
|
||||
import { FundingContext } from './context'
|
||||
import { cancelProfile } from './utils/fundProfiles'
|
||||
import MainCointainer from './components/MainCointainer'
|
||||
import { getTransfersMemo } from './selectors/pledging'
|
||||
import { getAndAddLpEvents } from './actions/lpEvents'
|
||||
import { getAndAddVaultEvents } from './actions/vaultEvents'
|
||||
import { addFormattedProfiles } from './actions/profiles'
|
||||
import { getAndAddPledges } from './actions/pledges'
|
||||
|
||||
const { getNetworkType } = web3.eth.net
|
||||
|
||||
|
@ -18,12 +18,7 @@ class App extends React.Component {
|
|||
state = {
|
||||
loading: true,
|
||||
lpAllowance: 0,
|
||||
fundProfiles: [],
|
||||
allPledges: [],
|
||||
needsInit: true,
|
||||
transfers: [],
|
||||
allLpEvents: [],
|
||||
vaultEvents: []
|
||||
};
|
||||
|
||||
componentDidMount(){
|
||||
|
@ -33,61 +28,38 @@ class App extends React.Component {
|
|||
const isInitialized = await vaultPledgingNeedsInit()
|
||||
if (!!isInitialized) {
|
||||
if (environment === 'development') console.log('mock_time:', await LiquidPledging.mock_time.call())
|
||||
|
||||
const lpAllowance = await getLpAllowance()
|
||||
const fundProfiles = await getProfileEvents()
|
||||
const allPledges = await getAllPledges()
|
||||
//TODO add block based sync
|
||||
const authorizedPayments = await getAuthorizedPayments()
|
||||
const account = await web3.eth.getCoinbase()
|
||||
const allLpEvents = await getAllLPEvents()
|
||||
const vaultEvents = await getAllVaultEvents()
|
||||
const transfers = getTransfersMemo({ allLpEvents })
|
||||
this.syncWithRemote()
|
||||
this.setState({
|
||||
account,
|
||||
network,
|
||||
environment,
|
||||
lpAllowance,
|
||||
fundProfiles,
|
||||
allPledges,
|
||||
authorizedPayments,
|
||||
allLpEvents,
|
||||
vaultEvents,
|
||||
transfers,
|
||||
needsInit: false,
|
||||
loading: false
|
||||
needsInit: false
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
appendFundProfile = async event => {
|
||||
const formattedEvent = await formatFundProfileEvent(event)
|
||||
this.setState((state) => {
|
||||
const { fundProfiles } = state
|
||||
return {
|
||||
...state,
|
||||
fundProfiles: [ ...fundProfiles, formattedEvent ]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
appendPledges = () => {
|
||||
const { allPledges } = this.state
|
||||
appendToExistingPledges(allPledges, this.setState)
|
||||
}
|
||||
|
||||
transferPledgeAmounts = tx => {
|
||||
transferBetweenPledges(this.setState.bind(this), tx)
|
||||
}
|
||||
|
||||
cancelFundProfile = id => {
|
||||
this.setState((state) => cancelProfile(state, id))
|
||||
async syncWithRemote() {
|
||||
// not running in parallel due to possible metamask / infura limitation
|
||||
await getAndAddLpEvents()
|
||||
await getAndAddVaultEvents()
|
||||
await getAndAddPledges()
|
||||
await addFormattedProfiles()
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, needsInit, lpAllowance, loading, fundProfiles, allPledges, allLpEvents, authorizedPayments, transfers, vaultEvents } = this.state
|
||||
const { appendFundProfile, appendPledges, transferPledgeAmounts, cancelFundProfile } = this
|
||||
const fundingContext = { allPledges, allLpEvents, appendPledges, appendFundProfile, account, transferPledgeAmounts, authorizedPayments, cancelFundProfile, fundProfiles, needsInit, initVaultAndLP, standardTokenApproval, transfers, vaultEvents }
|
||||
const { account, needsInit, lpAllowance, loading, authorizedPayments } = this.state
|
||||
const { appendFundProfile, appendPledges, transferPledgeAmounts } = this
|
||||
const fundingContext = { appendPledges, appendFundProfile, account, transferPledgeAmounts, authorizedPayments, needsInit, initVaultAndLP, standardTokenApproval }
|
||||
return (
|
||||
<FundingContext.Provider value={fundingContext}>
|
||||
<Router>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { Database } from '@nozbe/watermelondb'
|
||||
import LokiJSAdapter from '@nozbe/watermelondb/adapters/lokijs'
|
||||
|
||||
import schema from './model/schema'
|
||||
import LpEvent from './model/lpEvents'
|
||||
import VaultEvent from './model/vaultEvent'
|
||||
import Profile from './model/profile'
|
||||
import Pledge from './model/pledge'
|
||||
|
||||
const dbName = 'LiquidFunding'
|
||||
const adapter = new LokiJSAdapter({
|
||||
dbName,
|
||||
schema,
|
||||
})
|
||||
|
||||
const database = new Database({
|
||||
adapter,
|
||||
modelClasses: [
|
||||
LpEvent,
|
||||
VaultEvent,
|
||||
Profile,
|
||||
Pledge
|
||||
],
|
||||
actionsEnabled: true,
|
||||
})
|
||||
|
||||
export default database
|
|
@ -1,8 +1,11 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import DatabaseProvider from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import database from './db';
|
||||
import App from './dapp';
|
||||
|
||||
render(
|
||||
<App />,
|
||||
document.getElementById('app')
|
||||
<DatabaseProvider database={database}>
|
||||
<App />
|
||||
</DatabaseProvider>, document.getElementById('app')
|
||||
);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { Model } from '@nozbe/watermelondb'
|
||||
import { action, field, json } from '@nozbe/watermelondb/decorators'
|
||||
|
||||
|
||||
const sanitizeValues = json => json
|
||||
export default class LpEvent extends Model {
|
||||
static table = 'lp_events'
|
||||
|
||||
@field('address') address
|
||||
|
||||
@field('event_id') eventId
|
||||
|
||||
@field('event') event
|
||||
|
||||
@field('block_number') blockNumber
|
||||
|
||||
@json('return_values', sanitizeValues) returnValues
|
||||
|
||||
@action async addEvent(data) {
|
||||
return await this.create(lpEvent => {
|
||||
const { event, address, id, blockNumber } = data
|
||||
lpEvent.eventId = id
|
||||
lpEvent.address = address
|
||||
lpEvent.event = event
|
||||
lpEvent.blockNumber = blockNumber
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { action, field, relation } from '@nozbe/watermelondb/decorators'
|
||||
import { Q } from '@nozbe/watermelondb'
|
||||
import { LiquidModel } from '../utils/models'
|
||||
|
||||
|
||||
export default class Pledge extends LiquidModel {
|
||||
static table = 'pledges'
|
||||
static associations = {
|
||||
profiles: { type: 'belongs_to', key: 'profile_id' },
|
||||
}
|
||||
|
||||
@field('pledge_id') pledgeId
|
||||
@field('owner_id') owner
|
||||
@field('amount') amount
|
||||
@field('token') token
|
||||
@field('commit_time') commitTime
|
||||
@field('n_delegates') nDelegates
|
||||
@field('intended_project') intendedProject
|
||||
@field('pledge_state') pledgeState
|
||||
@relation('profiles', 'profile_id') profile
|
||||
|
||||
@action async transferTo(to, amount) {
|
||||
const toPledgeQuery = await this.collections.get('pledges').query(
|
||||
Q.where('pledge_id', to)
|
||||
).fetch()
|
||||
const toPledge = toPledgeQuery[0]
|
||||
await this.batch(
|
||||
this.prepareUpdate(pledge => {
|
||||
pledge.amount = (BigInt(pledge.amount) - BigInt(amount)).toString()
|
||||
}),
|
||||
toPledge.prepareUpdate(pledge => {
|
||||
pledge.amount = (BigInt(pledge.amount) + BigInt(amount)).toString()
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { action, field, children } from '@nozbe/watermelondb/decorators'
|
||||
import { LiquidModel } from '../utils/models'
|
||||
|
||||
|
||||
export default class Profile extends LiquidModel {
|
||||
static table = 'profiles'
|
||||
static associations = {
|
||||
pledges: { type: 'has_many', foreignKey: 'profile_id' }
|
||||
}
|
||||
|
||||
@field('addr') addr
|
||||
@field('event_id') eventId
|
||||
@field('canceled') canceled
|
||||
@field('commit_time') commitTime
|
||||
@field('type') type
|
||||
@field('name') name
|
||||
@field('url') url
|
||||
@field('id_profile') idProfile
|
||||
@children('pledges') pledges
|
||||
|
||||
@action async markAsCanceled() {
|
||||
await this.update(profile => {
|
||||
profile.canceled = true
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { appSchema, tableSchema } from '@nozbe/watermelondb'
|
||||
|
||||
export default appSchema({
|
||||
version: 1,
|
||||
tables: [
|
||||
tableSchema({
|
||||
name: 'lp_events',
|
||||
columns: [
|
||||
{ name: 'event_id', type: 'string', isIndexed: true },
|
||||
{ name: 'address', type: 'string' },
|
||||
{ name: 'event', type: 'string', isIndexed: true },
|
||||
{ name: 'block_number', type: 'number', isIndexed: true },
|
||||
{ name : 'return_values', type: 'string', isOptional: true }
|
||||
]
|
||||
}),
|
||||
tableSchema({
|
||||
name: 'vault_events',
|
||||
columns: [
|
||||
{ name: 'event_id', type: 'string', isIndexed: true },
|
||||
{ name: 'address', type: 'string' },
|
||||
{ name: 'event', type: 'string', isIndexed: true },
|
||||
{ name: 'block_number', type: 'number', isIndexed: true },
|
||||
{ name : 'return_values', type: 'string', isOptional: true }
|
||||
]
|
||||
}),
|
||||
tableSchema({
|
||||
name: 'profiles',
|
||||
columns: [
|
||||
{ name: 'event_id', type: 'string' },
|
||||
{ name: 'addr', type: 'string' },
|
||||
{ name: 'canceled', type: 'boolean' },
|
||||
{ name: 'commit_time', type: 'number' },
|
||||
{ name: 'type', type: 'string' },
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'url', type: 'string' },
|
||||
{ name: 'id_profile', type: 'number', isIndexed: true }
|
||||
]
|
||||
}),
|
||||
tableSchema({
|
||||
name: 'pledges',
|
||||
columns: [
|
||||
{ name: 'pledge_id', type: 'number', isIndexed: true },
|
||||
{ name: 'owner_id', type: 'number', isIndexed: true },
|
||||
{ name: 'amount', type: 'string' },
|
||||
{ name: 'token', type: 'string' },
|
||||
{ name: 'commit_time', type: 'number' },
|
||||
{ name: 'n_delegates', type: 'number' },
|
||||
{ name: 'intended_project', type: 'number' },
|
||||
{ name: 'pledge_state', type: 'number' },
|
||||
{ name: 'profile_id', type: 'string', isIndexed: true }
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
|
@ -0,0 +1,28 @@
|
|||
import { Model } from '@nozbe/watermelondb'
|
||||
import { action, field, json } from '@nozbe/watermelondb/decorators'
|
||||
|
||||
|
||||
const sanitizeValues = json => json
|
||||
export default class VaultEvent extends Model {
|
||||
static table = 'vault_events'
|
||||
|
||||
@field('address') address
|
||||
|
||||
@field('event_id') eventId
|
||||
|
||||
@field('event') event
|
||||
|
||||
@field('block_number') blockNumber
|
||||
|
||||
@json('return_values', sanitizeValues) returnValues
|
||||
|
||||
@action async addEvent(data) {
|
||||
return await this.create(lpEvent => {
|
||||
const { event, address, id, blockNumber } = data
|
||||
lpEvent.eventId = id
|
||||
lpEvent.address = address
|
||||
lpEvent.event = event
|
||||
lpEvent.blockNumber = blockNumber
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ const getWithdraws = state => state.vaultEvents.filter(
|
|||
event => event.event === 'AuthorizePayment'
|
||||
)
|
||||
export const getPledges = state => state.allPledges
|
||||
const pluckPledges = state => state.pledges
|
||||
|
||||
export const getTransfersMemo = createSelector(
|
||||
getTransfers,
|
||||
|
@ -20,7 +21,7 @@ export const getDeposits = transfers => transfers.filter(
|
|||
)
|
||||
|
||||
const getDepositsSelector = createSelector(
|
||||
getTransfersMemo,
|
||||
({ transfers }) => transfers,
|
||||
getDeposits
|
||||
)
|
||||
|
||||
|
@ -35,20 +36,28 @@ const pledgesWaitingCommit = pledges => {
|
|||
}
|
||||
|
||||
export const getPledgesWaitingCommit = createSelector(
|
||||
getPledges,
|
||||
pluckPledges,
|
||||
pledgesWaitingCommit
|
||||
)
|
||||
|
||||
const formatAndSumDepositWithdraws = (deposits, pledges, withdraws) => {
|
||||
const tokens = {}
|
||||
let incomplete = false
|
||||
deposits.forEach(deposit => {
|
||||
const { amount, to } = deposit.returnValues
|
||||
const { token } = pledges.find(p => Number(p.id) === Number(to))
|
||||
const pledge = pledges.find(p => Number(p.pledgeId) === Number(to))
|
||||
if (!pledge) {
|
||||
incomplete = true
|
||||
return
|
||||
}
|
||||
const { token } = pledge
|
||||
const tokenName = getTokenLabel(token)
|
||||
if (tokens[tokenName]) tokens[tokenName]['deposits'] = BigInt(tokens[tokenName]['deposits']) + BigInt(amount)
|
||||
else tokens[tokenName] = { 'deposits': BigInt(amount) }
|
||||
})
|
||||
|
||||
if (incomplete) return {}
|
||||
|
||||
withdraws
|
||||
.filter(w => !isNaN(Number(w.returnValues.ref.slice(2))))
|
||||
.forEach(withdraw => {
|
||||
|
@ -70,7 +79,7 @@ const formatAndSumDepositWithdraws = (deposits, pledges, withdraws) => {
|
|||
}
|
||||
export const getDepositWithdrawTotals = createSelector(
|
||||
getDepositsSelector,
|
||||
getPledges,
|
||||
pluckPledges,
|
||||
getWithdraws,
|
||||
formatAndSumDepositWithdraws
|
||||
)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export const fieldGenerator = self => (column, name) => {
|
||||
Object.defineProperty(self, name || column, {
|
||||
get() { return self._getRaw(column) },
|
||||
set(value) { self._setRaw(column, value) },
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
})
|
||||
}
|
||||
|
||||
export function initialize(target, name, descriptor) {
|
||||
descriptor.initializer = true
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
import LiquidPledging from 'Embark/contracts/LiquidPledging'
|
||||
import LPVault from 'Embark/contracts/LPVault'
|
||||
import web3 from 'Embark/web3'
|
||||
import { getLastBlockStored } from '../actions/lpEvents'
|
||||
|
||||
|
||||
const AUTHORIZE_PAYMENT = 'AuthorizePayment'
|
||||
const GIVER_ADDED = 'GiverAdded'
|
||||
const DELEGATE_ADDED = 'DelegateAdded'
|
||||
const PROJECT_ADDED = 'ProjectAdded'
|
||||
export const GIVER_ADDED = 'GiverAdded'
|
||||
export const DELEGATE_ADDED = 'DelegateAdded'
|
||||
export const PROJECT_ADDED = 'ProjectAdded'
|
||||
const ALL_EVENTS = 'allEvents'
|
||||
const lookups = {
|
||||
[GIVER_ADDED]: {
|
||||
|
@ -31,10 +33,10 @@ const formatVaultEvent = async event => {
|
|||
}
|
||||
}
|
||||
|
||||
const getPastVaultEvents = async (event, raw = false) => {
|
||||
const getPastVaultEvents = async (event, raw = false, fromBlock = 0) => {
|
||||
const events = await LPVault.getPastEvents(event, {
|
||||
addr: await web3.eth.getCoinbase(),
|
||||
fromBlock: 0,
|
||||
fromBlock,
|
||||
toBlock: 'latest'
|
||||
})
|
||||
if (raw) return events
|
||||
|
@ -47,12 +49,12 @@ const getPastVaultEvents = async (event, raw = false) => {
|
|||
const { getPledgeAdmin } = LiquidPledging.methods
|
||||
export const formatFundProfileEvent = async event => {
|
||||
const lookup = lookups[event.event]
|
||||
const { returnValues: { url, idProject } } = event
|
||||
const { id, returnValues: { url } } = event
|
||||
const idProfile = event.returnValues[lookup.id]
|
||||
const { addr, commitTime, name, canceled } = await getPledgeAdmin(idProfile).call()
|
||||
return {
|
||||
id,
|
||||
idProfile,
|
||||
idProject,
|
||||
url,
|
||||
commitTime,
|
||||
name,
|
||||
|
@ -62,10 +64,10 @@ export const formatFundProfileEvent = async event => {
|
|||
}
|
||||
}
|
||||
|
||||
const getPastEvents = async (event, raw = false) => {
|
||||
const getPastEvents = async (event, raw = false, fromBlock = 0) => {
|
||||
const events = await LiquidPledging.getPastEvents(event, {
|
||||
addr: await web3.eth.getCoinbase(),
|
||||
fromBlock: 0,
|
||||
fromBlock,
|
||||
toBlock: 'latest'
|
||||
})
|
||||
if (raw) return events
|
||||
|
@ -86,9 +88,13 @@ export const lpEventsSubscription = async () => {
|
|||
export const getFunderProfiles = async () => await getPastEvents(GIVER_ADDED)
|
||||
export const getDelegateProfiles = async () => await getPastEvents(DELEGATE_ADDED)
|
||||
export const getProjectProfiles = async () => await getPastEvents(PROJECT_ADDED)
|
||||
export const getAllLPEvents = async () => await getPastEvents(ALL_EVENTS, true)
|
||||
export const getAllLPEvents = async fromBlock => await getPastEvents(
|
||||
ALL_EVENTS,
|
||||
true,
|
||||
fromBlock
|
||||
)
|
||||
export const getAuthorizedPayments = async () => getPastVaultEvents(AUTHORIZE_PAYMENT)
|
||||
export const getAllVaultEvents = async () => getPastVaultEvents(ALL_EVENTS,true)
|
||||
export const getAllVaultEvents = async (fromBlock = 0) => getPastVaultEvents(ALL_EVENTS,true, fromBlock)
|
||||
export const getProfileEvents = async () => {
|
||||
const [ funderProfiles, delegateProfiles, projectProfiles]
|
||||
= await Promise.all([getFunderProfiles(), getDelegateProfiles(), getProjectProfiles()])
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { Model } from '@nozbe/watermelondb'
|
||||
|
||||
export function getFields(obj) {
|
||||
const validTypes = new Set(['string', 'number', 'boolean'])
|
||||
const newObj = {}
|
||||
const proto = Object.getPrototypeOf(obj)
|
||||
const names = Object.getOwnPropertyNames(proto)
|
||||
names
|
||||
.filter(name => validTypes.has(typeof obj[name]))
|
||||
.forEach(name => { newObj[name] = obj[name] })
|
||||
return newObj
|
||||
}
|
||||
|
||||
export class LiquidModel extends Model {
|
||||
getFields() {
|
||||
const validTypes = new Set(['string', 'number', 'boolean'])
|
||||
const newObj = {}
|
||||
const proto = Object.getPrototypeOf(this)
|
||||
const names = Object.getOwnPropertyNames(proto)
|
||||
names
|
||||
.filter(name => validTypes.has(typeof this[name]))
|
||||
.forEach(name => { newObj[name] = this[name] })
|
||||
return newObj
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This source code was adapted from:
|
||||
* https://github.com/facebook/create-react-app/blob/v2.0.4/packages/babel-preset-react-app/webpack-overrides.js
|
||||
*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* The MIT license for this code may be found on GitHub:
|
||||
* https://github.com/facebook/create-react-app/blob/v2.0.4/packages/babel-preset-react-app/LICENSE
|
||||
*/
|
||||
|
||||
const crypto = require('crypto');
|
||||
const macroCheck = new RegExp('[./]macro');
|
||||
|
||||
module.exports = function () {
|
||||
return {
|
||||
config(config, {source}) {
|
||||
// don't cache babel macros
|
||||
// https://github.com/babel/babel/issues/8497
|
||||
if (macroCheck.test(source)) {
|
||||
return Object.assign({}, config.options, {
|
||||
caller: Object.assign({}, config.options.caller, {
|
||||
macroInvalidationToken: crypto.randomBytes(32).toString('hex')
|
||||
})
|
||||
});
|
||||
}
|
||||
return config.options;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -49,13 +49,16 @@
|
|||
"mocha": "^3.5.0",
|
||||
"random-bytes": "^1.0.0",
|
||||
"solcpiler": "1.0.0-beta.8",
|
||||
"web3": "1.0.0-beta.34"
|
||||
"web3": "1.0.0-beta.34",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/Giveth/liquidpledging#readme",
|
||||
"dependencies": {
|
||||
"@aragon/os": "3.1.9",
|
||||
"@material-ui/core": "^3.6.0",
|
||||
"@material-ui/icons": "^3.0.1",
|
||||
"@nozbe/watermelondb": "^0.9.0",
|
||||
"@nozbe/with-observables": "^1.0.2",
|
||||
"async": "^2.4.0",
|
||||
"chai": "^4.1.0",
|
||||
"chart.js": "^2.7.3",
|
||||
|
@ -66,6 +69,7 @@
|
|||
"eslint": "^5.9.0",
|
||||
"eth-contract-class": "^0.0.12",
|
||||
"formik": "^1.3.2",
|
||||
"lokijs": "^1.5.6",
|
||||
"material-table": "^1.12.0",
|
||||
"ramda": "^0.26.1",
|
||||
"react": "^16.7.0",
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
/* global __dirname module process require */
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const dappPath = process.env.DAPP_PATH;
|
||||
const embarkPath = process.env.EMBARK_PATH;
|
||||
|
||||
const dappNodeModules = path.join(dappPath, 'node_modules');
|
||||
const embarkNodeModules = path.join(embarkPath, 'node_modules');
|
||||
let nodePathNodeModules;
|
||||
if (process.env.NODE_PATH) {
|
||||
nodePathNodeModules = process.env.NODE_PATH.split(path.delimiter);
|
||||
} else {
|
||||
nodePathNodeModules = [];
|
||||
}
|
||||
if (!nodePathNodeModules.includes(embarkNodeModules)) {
|
||||
nodePathNodeModules.unshift(embarkNodeModules);
|
||||
}
|
||||
|
||||
function requireFromEmbark(mod) {
|
||||
return require(requireFromEmbark.resolve(mod));
|
||||
}
|
||||
|
||||
requireFromEmbark.resolve = function (mod) {
|
||||
return require.resolve(
|
||||
mod,
|
||||
{paths: [embarkNodeModules]}
|
||||
);
|
||||
};
|
||||
|
||||
const cloneDeep = requireFromEmbark('lodash.clonedeep');
|
||||
const glob = requireFromEmbark('glob');
|
||||
const HardSourceWebpackPlugin = requireFromEmbark('hard-source-webpack-plugin');
|
||||
|
||||
const embarkAliases = require(path.join(dappPath, '.embark/embark-aliases.json'));
|
||||
const embarkAssets = require(path.join(dappPath, '.embark/embark-assets.json'));
|
||||
const embarkJson = require(path.join(dappPath, 'embark.json'));
|
||||
const embarkPipeline = require(path.join(dappPath, '.embark/embark-pipeline.json'));
|
||||
|
||||
const buildDir = path.join(dappPath, embarkJson.buildDir);
|
||||
|
||||
// it's important to `embark reset` if a pkg version is specified in
|
||||
// embark.json and changed/removed later, otherwise pkg resolution may behave
|
||||
// unexpectedly
|
||||
let versions;
|
||||
try {
|
||||
versions = glob.sync(path.join(dappPath, '.embark/versions/*/*'));
|
||||
} catch (e) {
|
||||
versions = [];
|
||||
}
|
||||
|
||||
const entry = Object.keys(embarkAssets)
|
||||
.filter(key => key.match(/\.js$/))
|
||||
.reduce((obj, key) => {
|
||||
// webpack entry paths should start with './' if they're relative to the
|
||||
// webpack context; embark.json "app" keys correspond to lists of .js
|
||||
// source paths relative to the top-level dapp dir and may be missing the
|
||||
// leading './'
|
||||
obj[key] = embarkAssets[key]
|
||||
.map(file => {
|
||||
let file_path = file.path;
|
||||
if (!file.path.match(/^\.\//)) {
|
||||
file_path = './' + file_path;
|
||||
}
|
||||
return file_path;
|
||||
});
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
function resolve(pkgName) {
|
||||
if (Array.isArray(pkgName)) {
|
||||
const _pkgName = pkgName[0];
|
||||
pkgName[0] = requireFromEmbark.resolve(_pkgName);
|
||||
return pkgName;
|
||||
}
|
||||
return requireFromEmbark.resolve(pkgName);
|
||||
}
|
||||
|
||||
// base config
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// order and options of babel plugins and presets adapted from babel-preset-react-app:
|
||||
// see: https://github.com/facebook/create-react-app/tree/v2.0.4/packages/babel-preset-react-app
|
||||
// + babel plugins run before babel presets.
|
||||
// + babel plugin ordering is first to last.
|
||||
// + babel preset ordering is reversed (last to first).
|
||||
// see: https://babeljs.io/docs/en/plugins#plugin-ordering
|
||||
|
||||
const base = {
|
||||
context: dappPath,
|
||||
entry: entry,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
|
||||
loader: 'url-loader?limit=100000'
|
||||
},
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /(node_modules|bower_components|\.embark[\\/]versions)/,
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
cacheCompression: false,
|
||||
customize: path.join(__dirname, 'babel-loader-overrides.js'),
|
||||
plugins: [
|
||||
[
|
||||
'babel-plugin-module-resolver', {
|
||||
alias: embarkAliases
|
||||
}
|
||||
],
|
||||
'babel-plugin-macros',
|
||||
'@babel/plugin-transform-destructuring',
|
||||
[
|
||||
'@babel/plugin-proposal-decorators', {
|
||||
legacy: true
|
||||
}
|
||||
],
|
||||
[
|
||||
'@babel/plugin-proposal-class-properties', {
|
||||
loose: true
|
||||
}
|
||||
],
|
||||
[
|
||||
'@babel/plugin-proposal-object-rest-spread', {
|
||||
useBuiltIns: true
|
||||
}
|
||||
],
|
||||
[
|
||||
'@babel/plugin-transform-runtime', {
|
||||
helpers: true,
|
||||
regenerator: true
|
||||
}
|
||||
],
|
||||
'@babel/plugin-syntax-dynamic-import'
|
||||
].map(resolve),
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env', {
|
||||
exclude: ['transform-typeof-symbol'],
|
||||
modules: false,
|
||||
targets: {
|
||||
browsers: ['last 1 version', 'not dead', '> 0.2%']
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'@babel/preset-react', {
|
||||
useBuiltIns: true
|
||||
}
|
||||
]
|
||||
].map(resolve)
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.worker\.js$/,
|
||||
use: { loader: 'worker-loader' }
|
||||
}
|
||||
]
|
||||
},
|
||||
output: {
|
||||
filename: (chunkData) => chunkData.chunk.name,
|
||||
// globalObject workaround for node-compatible UMD builds with webpack 4
|
||||
// see: https://github.com/webpack/webpack/issues/6522#issuecomment-371120689
|
||||
// see: https://github.com/webpack/webpack/issues/6522#issuecomment-418864518
|
||||
globalObject: '(typeof self !== \'undefined\' ? self : this)',
|
||||
libraryTarget: 'umd',
|
||||
path: buildDir
|
||||
},
|
||||
plugins: [new HardSourceWebpackPlugin()],
|
||||
// profiling and generating verbose stats increases build time; if stats
|
||||
// are generated embark will write the output to:
|
||||
// path.join(dappPath, '.embark/stats.[json,report]')
|
||||
// to visualize the stats info in a browser run:
|
||||
// npx webpack-bundle-analyzer .embark/stats.json <buildDir>
|
||||
profile: true, stats: 'verbose',
|
||||
resolve: {
|
||||
alias: embarkAliases,
|
||||
extensions: [
|
||||
// webpack defaults
|
||||
// see: https://webpack.js.org/configuration/resolve/#resolve-extensions
|
||||
'.wasm', '.mjs', '.js', '.json',
|
||||
// additional extensions
|
||||
'.jsx'
|
||||
],
|
||||
modules: [
|
||||
...versions,
|
||||
'node_modules',
|
||||
dappNodeModules,
|
||||
...nodePathNodeModules
|
||||
]
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: [
|
||||
'node_modules',
|
||||
dappNodeModules,
|
||||
...nodePathNodeModules
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const baseBabelLoader = base.module.rules[3];
|
||||
|
||||
// Flow
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// should be false in configs that have isTypeScriptEnabled = true
|
||||
// const isFlowEnabled = !embarkPipeline.typescript;
|
||||
// if (isFlowEnabled) {
|
||||
// // position @babel/plugin-transform-flow-strip-types per babel-preset-react-app
|
||||
// baseBabelLoader.options.plugins.unshift(
|
||||
// requireFromEmbark.resolve('@babel/plugin-transform-flow-strip-types')
|
||||
// );
|
||||
// }
|
||||
|
||||
// TypeScript
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// should be false in configs that have isFlowEnabled = true
|
||||
const isTypeScriptEnabled = !!embarkPipeline.typescript;
|
||||
if (isTypeScriptEnabled) {
|
||||
// position @babel/preset-typescript as the last preset (runs first)
|
||||
// see: https://blogs.msdn.microsoft.com/typescript/2018/08/27/typescript-and-babel-7/
|
||||
baseBabelLoader.options.presets.push(
|
||||
requireFromEmbark.resolve('@babel/preset-typescript')
|
||||
);
|
||||
// additional extensions
|
||||
baseBabelLoader.test = /\.(js|ts)x?$/;
|
||||
base.resolve.extensions.push('.ts', '.tsx');
|
||||
}
|
||||
|
||||
// if (isFlowEnabled && isTypeScriptEnabled) {
|
||||
// throw new Error('isFlowEnabled and isTypeScriptEnabled cannot both be true');
|
||||
// }
|
||||
|
||||
// development config
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const development = cloneDeep(base);
|
||||
// full source maps increase build time but are useful during dapp development
|
||||
development.devtool = 'source-map';
|
||||
development.mode = 'development';
|
||||
// alternatively:
|
||||
// development.mode = 'none';
|
||||
development.name = 'development';
|
||||
const devBabelLoader = development.module.rules[3];
|
||||
devBabelLoader.options.compact = false;
|
||||
// enable 'development' option for @babel/preset-react
|
||||
const devPresetReact = devBabelLoader.options.presets[1];
|
||||
const devPresetReactOptions = devPresetReact[1];
|
||||
devPresetReactOptions.development = true;
|
||||
|
||||
// production config
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const production = cloneDeep(base);
|
||||
production.mode = 'production';
|
||||
production.name = 'production';
|
||||
const prodBabelLoader = production.module.rules[3];
|
||||
// position babel-plugin-transform-react-remove-prop-types per babel-preset-react-app
|
||||
prodBabelLoader.options.plugins.splice(prodBabelLoader.length - 1, 0, [
|
||||
requireFromEmbark.resolve('babel-plugin-transform-react-remove-prop-types'),
|
||||
{
|
||||
removeImport: true
|
||||
}
|
||||
]);
|
||||
|
||||
// export a list of named configs
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
module.exports = [
|
||||
development,
|
||||
production
|
||||
];
|
96
yarn.lock
96
yarn.lock
|
@ -321,6 +321,23 @@
|
|||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
||||
|
||||
"@nozbe/watermelondb@^0.9.0":
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@nozbe/watermelondb/-/watermelondb-0.9.0.tgz#2864c11228a981dddf96d94c793082a90cd002f6"
|
||||
integrity sha512-jAjmtATFPAD8gE0pqLVsCT5GVA/2H5poH+Rxy/pbukARrlty5nI38FUP7+yNYiSMkPp0tSCHDMiFfkMzRAjxzQ==
|
||||
dependencies:
|
||||
rambdax "^0.23.0"
|
||||
rxjs "^6.2.2"
|
||||
rxjs-compat "^6.3.2"
|
||||
sql-escape-string "^1.1.0"
|
||||
|
||||
"@nozbe/with-observables@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@nozbe/with-observables/-/with-observables-1.0.2.tgz#9749b3b5d33a058f8aed92d75138a5778bead89b"
|
||||
integrity sha512-p9WNGTUm0eKb28ylcMBayUgBbzGoZ2bVBxzqtS5SxRL3Hopwf1eSNF+fGbC8fX1YHxhQjma4nN4pkKAt+H6NLA==
|
||||
dependencies:
|
||||
rxjs "^6.2.2"
|
||||
|
||||
"@types/jss@^9.5.6":
|
||||
version "9.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.7.tgz#fa57a6d0b38a3abef8a425e3eb6a53495cb9d5a0"
|
||||
|
@ -409,6 +426,21 @@ aes-js@^3.1.1:
|
|||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a"
|
||||
|
||||
ajv-keywords@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
|
||||
integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
|
||||
|
||||
ajv@^6.1.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96"
|
||||
integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==
|
||||
dependencies:
|
||||
fast-deep-equal "^2.0.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ajv@^6.5.3, ajv@^6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.1.tgz#6360f5ed0d80f232cc2b294c362d5dc2e538dd61"
|
||||
|
@ -1218,6 +1250,11 @@ bcrypt-pbkdf@^1.0.0:
|
|||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
"bignumber.js@git+https://github.com/debris/bignumber.js#master":
|
||||
version "2.0.7"
|
||||
resolved "git+https://github.com/debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9"
|
||||
|
@ -2551,6 +2588,11 @@ emoji-regex@^6.5.1:
|
|||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
|
||||
|
||||
emojis-list@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
|
||||
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
|
@ -4523,6 +4565,13 @@ json5@^0.5.1:
|
|||
version "0.5.1"
|
||||
resolved "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
|
||||
|
||||
json5@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
||||
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
jsonfile@^2.1.0:
|
||||
version "2.4.0"
|
||||
resolved "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
|
||||
|
@ -4836,6 +4885,15 @@ load-json-file@^4.0.0:
|
|||
pify "^3.0.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
loader-utils@^1.0.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
|
||||
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^2.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
@ -4934,6 +4992,11 @@ lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1,
|
|||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
|
||||
lokijs@^1.5.6:
|
||||
version "1.5.6"
|
||||
resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.6.tgz#6de6b8c3ff7a972fd0104169f81e7ddc244c029f"
|
||||
integrity sha512-xJoDXy8TASTjmXMKr4F8vvNUCu4dqlwY5gmn0g5BajGt1GM3goDCafNiGAh/sfrWgkfWu1J4OfsxWm8yrWweJA==
|
||||
|
||||
looper@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec"
|
||||
|
@ -5969,6 +6032,11 @@ quick-lru@^1.0.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
|
||||
|
||||
rambdax@^0.23.0:
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/rambdax/-/rambdax-0.23.0.tgz#a4ffe1a6bbe0a85e164bd2e7187ac3453f60efc3"
|
||||
integrity sha512-TquZA91VfG2he/iFExaMDfMHN/0B4U0H2LEoGwixYc7BmKMp6mj2gWfIpcjrUroPuxc6ExbXK9JvnKN5t5kgFQ==
|
||||
|
||||
ramda@^0.26.1:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
|
@ -6513,7 +6581,12 @@ rx-lite@*, rx-lite@^4.0.8:
|
|||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
|
||||
|
||||
rxjs@^6.1.0:
|
||||
rxjs-compat@^6.3.2:
|
||||
version "6.3.3"
|
||||
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.3.3.tgz#2ab3b9ac0dac0c073749d55fef9c03ea1df2045c"
|
||||
integrity sha512-caGN7ixiabHpOofginKEquuHk7GgaCrC7UpUQ9ZqGp80tMc68msadOeP/2AKy2R4YJsT1+TX5GZCtxO82qWkyA==
|
||||
|
||||
rxjs@^6.1.0, rxjs@^6.2.2:
|
||||
version "6.3.3"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55"
|
||||
integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==
|
||||
|
@ -6552,6 +6625,14 @@ scheduler@^0.12.0:
|
|||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
schema-utils@^0.4.0:
|
||||
version "0.4.7"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
|
||||
integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==
|
||||
dependencies:
|
||||
ajv "^6.1.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
scrypt.js@0.2.0, scrypt.js@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada"
|
||||
|
@ -6867,6 +6948,11 @@ sprintf-js@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
sql-escape-string@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sql-escape-string/-/sql-escape-string-1.1.0.tgz#fe744b8514868c0eb4bfb9e4a989271d40f30eb9"
|
||||
integrity sha1-/nRLhRSGjA60v7nkqYknHUDzDrk=
|
||||
|
||||
sshpk@^1.7.0:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.2.tgz#c946d6bd9b1a39d0e8635763f5242d6ed6dcb629"
|
||||
|
@ -8057,6 +8143,14 @@ wordwrap@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
|
||||
|
||||
worker-loader@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-2.0.0.tgz#45fda3ef76aca815771a89107399ee4119b430ac"
|
||||
integrity sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==
|
||||
dependencies:
|
||||
loader-utils "^1.0.0"
|
||||
schema-utils "^0.4.0"
|
||||
|
||||
wrap-ansi@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
|
||||
|
|
Loading…
Reference in New Issue