inv & with satistics + filtering

This commit is contained in:
keyan 2021-12-16 11:27:12 -06:00
parent 06f5ed731e
commit d92f58aaf4
9 changed files with 159 additions and 84 deletions

View File

@ -49,18 +49,18 @@ export default {
connectAddress: async (parent, args, { lnd }) => {
return process.env.LND_CONNECT_ADDRESS
},
walletHistory: async (parent, { cursor }, { me, models, lnd }) => {
walletHistory: async (parent, { cursor, inc }, { me, models, lnd }) => {
const decodedCursor = decodeCursor(cursor)
if (!me) {
throw new AuthenticationError('you must be logged in')
}
// TODO
// 1. union invoices and withdrawals (check)
// 2. add to union spending and receiving
const include = new Set(inc.split(','))
const queries = []
let history = await models.$queryRaw(`
(SELECT id, bolt11, created_at as "createdAt",
if (include.has('invoice')) {
queries.push(
`(SELECT ('invoice' || id) as id, id as "factId", bolt11, created_at as "createdAt",
COALESCE("msatsReceived", "msatsRequested") as msats, NULL as "msatsFee",
CASE WHEN "confirmedAt" IS NOT NULL THEN 'CONFIRMED'
WHEN "expiresAt" <= $2 THEN 'EXPIRED'
@ -69,11 +69,12 @@ export default {
'invoice' as type
FROM "Invoice"
WHERE "userId" = $1
AND created_at <= $2
ORDER BY created_at desc
LIMIT ${LIMIT}+$3)
UNION ALL
(SELECT id, bolt11, created_at as "createdAt",
AND created_at <= $2)`)
}
if (include.has('withdrawal')) {
queries.push(
`(SELECT ('withdrawal' || id) as id, id as "factId", bolt11, created_at as "createdAt",
CASE WHEN status = 'CONFIRMED' THEN "msatsPaid"
ELSE "msatsPaying" END as msats,
CASE WHEN status = 'CONFIRMED' THEN "msatsFeePaid"
@ -82,13 +83,28 @@ export default {
'withdrawal' as type
FROM "Withdrawl"
WHERE "userId" = $1
AND created_at <= $2
ORDER BY created_at desc
LIMIT ${LIMIT}+$3)
AND created_at <= $2)`)
}
// TODO
// 1. union invoices and withdrawals (check)
// 2. add to union spending and receiving
if (queries.length === 0) {
return {
cursor: null,
facts: []
}
}
let history = await models.$queryRaw(`
${queries.join(' UNION ALL ')}
ORDER BY "createdAt" DESC
OFFSET $3
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
console.log(history)
history = history.map(f => {
if (f.bolt11) {
const inv = lnpr.decode(f.bolt11)
@ -113,6 +129,8 @@ export default {
return f
})
console.log(history)
return {
cursor: history.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
facts: history

View File

@ -30,7 +30,7 @@ export default async function getSSRApolloClient (req, me = null) {
}
export function getGetServerSideProps (query, variables = null, foundField) {
return async function ({ req, params }) {
return async function ({ req, query: params }) {
const client = await getSSRApolloClient(req)
const { error, data } = await client.query({
query,

View File

@ -5,7 +5,7 @@ export default gql`
invoice(id: ID!): Invoice!
withdrawl(id: ID!): Withdrawl!
connectAddress: String!
walletHistory(cursor: String): History
walletHistory(cursor: String, inc: String): History
}
extend type Mutation {
@ -41,6 +41,7 @@ export default gql`
type Fact {
id: ID!
factId: ID!
bolt11: String
createdAt: String!
msats: Int!

View File

@ -175,16 +175,16 @@ export function Input ({ label, groupClassName, ...props }) {
)
}
export function Checkbox ({ children, label, extra, handleChange, ...props }) {
export function Checkbox ({ children, label, extra, handleChange, inline, ...props }) {
// React treats radios and checkbox inputs differently other input types, select, and textarea.
// Formik does this too! When you specify `type` to useField(), it will
// return the correct bag of props for you
const [field, { value }] = useField({ ...props, type: 'checkbox' })
const [field] = useField({ ...props, type: 'checkbox' })
return (
<div className={value ? styles.checkboxChecked : styles.checkboxUnchecked}>
<BootstrapForm.Check
custom
id={props.id || props.name}
inline={inline}
>
<BootstrapForm.Check.Input
{...field} {...props} type='checkbox' onChange={(e) => {
@ -200,8 +200,6 @@ export function Checkbox ({ children, label, extra, handleChange, ...props }) {
</div>}
</BootstrapForm.Check.Label>
</BootstrapForm.Check>
{children}
</div>
)
}

View File

@ -113,7 +113,7 @@ export default function UserHeader ({ user }) {
</div>
<Nav
className={styles.nav}
activeKey={router.asPath}
activeKey={router.asPath.split('?')[0]}
>
<Nav.Item>
<Link href={'/' + user.name} passHref>
@ -132,8 +132,8 @@ export default function UserHeader ({ user }) {
</Nav.Item>
{isMe &&
<Nav.Item>
<Link href='/satistics' passHref>
<Nav.Link>satistics</Nav.Link>
<Link href='/satistics?inc=invoice,withdrawal,stacked,spent' passHref>
<Nav.Link eventKey='/satistics'>satistics</Nav.Link>
</Link>
</Nav.Item>}
</Nav>

View File

@ -25,10 +25,11 @@ export const WITHDRAWL = gql`
}`
export const WALLET_HISTORY = gql`
query WalletHistory($cursor: String) {
walletHistory(cursor: $cursor) {
query WalletHistory($cursor: String, $inc: String) {
walletHistory(cursor: $cursor, inc: $inc) {
facts {
id
factId
type
createdAt
msats

View File

@ -66,7 +66,7 @@ export default function getApolloClient () {
}
},
walletHistory: {
keyArgs: false,
keyArgs: ['inc'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.facts)) {
return incoming

View File

@ -12,6 +12,8 @@ import styles from '../styles/satistics.module.css'
import Moon from '../svgs/moon-fill.svg'
import Check from '../svgs/check-double-line.svg'
import ThumbDown from '../svgs/thumb-down-fill.svg'
import { Checkbox, Form } from '../components/form'
import { useRouter } from 'next/router'
export const getServerSideProps = getGetServerSideProps(WALLET_HISTORY)
@ -83,7 +85,29 @@ function Satus ({ status }) {
export default function Satistics ({ data: { walletHistory: { facts, cursor } } }) {
const me = useMe()
const { value: darkMode } = useDarkMode()
const { data, fetchMore } = useQuery(WALLET_HISTORY)
const router = useRouter()
const { data, fetchMore } = useQuery(WALLET_HISTORY, { variables: { inc: router.query.inc } })
console.log(router.query.inc, data)
function filterRoutePush (filter, add) {
const inc = new Set(router.query.inc.split(','))
inc.delete('')
// depending on addrem, add or remove filter
if (add) {
inc.add(filter)
} else {
inc.delete(filter)
}
const incstr = [...inc].join(',')
router.push(`/satistics?inc=${incstr}`)
}
function included (filter) {
const inc = new Set(router.query.inc.split(','))
return inc.has(filter)
}
if (data) {
({ walletHistory: { facts, cursor } } = data)
@ -97,6 +121,34 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
return (
<Layout noSeo>
<UserHeader user={me} />
<div className='mt-3'>
<Form
initial={{
invoice: included('invoice'),
withdrawal: included('withdrawal'),
stacked: included('stacked'),
spent: included('spent')
}}
>
<div className='d-flex justify-content-around flex-wrap'>
<Checkbox
label='invoice' name='invoice' inline
handleChange={c => filterRoutePush('invoice', c)}
/>
<Checkbox
label='withdrawal' name='withdrawal' inline
handleChange={c => filterRoutePush('withdrawal', c)}
/>
<Checkbox
label='stacked' name='stacked' inline
handleChange={c => filterRoutePush('stacked', c)}
/>
<Checkbox
label='spent' name='spent' inline
handleChange={c => filterRoutePush('spent', c)}
/>
</div>
</Form>
<Table className='mt-3 mb-0' bordered hover size='sm' variant={darkMode ? 'dark' : undefined}>
<thead>
<tr>
@ -107,7 +159,7 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
</thead>
<tbody>
{facts.map((f, i) => (
<Link href={`${f.type}s/${f.id}`} key={`${f.type}-${f.id}`}>
<Link href={`${f.type}s/${f.factId}`} key={f.id}>
<tr className={styles.row}>
<td className={`${styles.type} ${satusClass(f.status)}`}>{f.type}</td>
<td className={styles.description}>
@ -123,6 +175,7 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
</tbody>
</Table>
<MoreFooter cursor={cursor} fetchMore={fetchMore} Skeleton={SatisticsSkeleton} />
</div>
</Layout>
)
}

View File

@ -64,6 +64,10 @@ $tooltip-bg: #5c8001;
line-height: 1.2rem;
}
.custom-checkbox.custom-control-inline {
margin-right: .5rem;
}
.table {
color: var(--theme-color);
background-color: var(--theme-body);