satistics done
This commit is contained in:
parent
d92f58aaf4
commit
bbc34edf51
@ -3,6 +3,7 @@ import { UserInputError, AuthenticationError } from 'apollo-server-micro'
|
|||||||
import serialize from './serial'
|
import serialize from './serial'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
||||||
import lnpr from 'bolt11'
|
import lnpr from 'bolt11'
|
||||||
|
import { SELECT } from './item'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
@ -86,9 +87,29 @@ export default {
|
|||||||
AND created_at <= $2)`)
|
AND created_at <= $2)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
if (include.has('stacked')) {
|
||||||
// 1. union invoices and withdrawals (check)
|
queries.push(
|
||||||
// 2. add to union spending and receiving
|
`(SELECT ('stacked' || "Item".id) as id, "Item".id as "factId", NULL as bolt11,
|
||||||
|
MAX("ItemAct".created_at) as "createdAt", sum("ItemAct".sats) * 1000 as msats,
|
||||||
|
0 as "msatsFee", NULL as status, 'stacked' as type
|
||||||
|
FROM "ItemAct"
|
||||||
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
||||||
|
WHERE "ItemAct"."userId" <> $1 AND "ItemAct".act <> 'BOOST'
|
||||||
|
AND "Item"."userId" = $1 AND "ItemAct".created_at <= $2
|
||||||
|
GROUP BY "Item".id)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (include.has('spent')) {
|
||||||
|
queries.push(
|
||||||
|
`(SELECT ('spent' || "Item".id) as id, "Item".id as "factId", NULL as bolt11,
|
||||||
|
MAX("ItemAct".created_at) as "createdAt", sum("ItemAct".sats) * 1000 as msats,
|
||||||
|
0 as "msatsFee", NULL as status, 'spent' as type
|
||||||
|
FROM "ItemAct"
|
||||||
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
||||||
|
WHERE "ItemAct"."userId" = $1
|
||||||
|
AND "ItemAct".created_at <= $2
|
||||||
|
GROUP BY "Item".id)`)
|
||||||
|
}
|
||||||
|
|
||||||
if (queries.length === 0) {
|
if (queries.length === 0) {
|
||||||
return {
|
return {
|
||||||
@ -103,8 +124,6 @@ export default {
|
|||||||
OFFSET $3
|
OFFSET $3
|
||||||
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
|
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
|
||||||
|
|
||||||
console.log(history)
|
|
||||||
|
|
||||||
history = history.map(f => {
|
history = history.map(f => {
|
||||||
if (f.bolt11) {
|
if (f.bolt11) {
|
||||||
const inv = lnpr.decode(f.bolt11)
|
const inv = lnpr.decode(f.bolt11)
|
||||||
@ -120,6 +139,9 @@ export default {
|
|||||||
}
|
}
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case 'withdrawal':
|
case 'withdrawal':
|
||||||
|
f.msats = (-1 * f.msats) - f.msatsFee
|
||||||
|
break
|
||||||
|
case 'spent':
|
||||||
f.msats *= -1
|
f.msats *= -1
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
@ -129,8 +151,6 @@ export default {
|
|||||||
return f
|
return f
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(history)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cursor: history.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
cursor: history.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
||||||
facts: history
|
facts: history
|
||||||
@ -251,5 +271,19 @@ export default {
|
|||||||
satsPaid: w => Math.floor(w.msatsPaid / 1000),
|
satsPaid: w => Math.floor(w.msatsPaid / 1000),
|
||||||
satsFeePaying: w => Math.floor(w.msatsFeePaying / 1000),
|
satsFeePaying: w => Math.floor(w.msatsFeePaying / 1000),
|
||||||
satsFeePaid: w => Math.floor(w.msatsFeePaid / 1000)
|
satsFeePaid: w => Math.floor(w.msatsFeePaid / 1000)
|
||||||
|
},
|
||||||
|
|
||||||
|
Fact: {
|
||||||
|
item: async (fact, args, { models }) => {
|
||||||
|
if (fact.type !== 'spent' && fact.type !== 'stacked') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const [item] = await models.$queryRaw(`
|
||||||
|
${SELECT}
|
||||||
|
FROM "Item"
|
||||||
|
WHERE id = $1`, Number(fact.factId))
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,10 @@ export default gql`
|
|||||||
createdAt: String!
|
createdAt: String!
|
||||||
msats: Int!
|
msats: Int!
|
||||||
msatsFee: Int
|
msatsFee: Int
|
||||||
status: String!
|
status: String
|
||||||
type: String!
|
type: String!
|
||||||
description: String
|
description: String
|
||||||
|
item: Item
|
||||||
}
|
}
|
||||||
|
|
||||||
type History {
|
type History {
|
||||||
|
@ -38,9 +38,14 @@ function Parent ({ item, rootText }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const truncateString = (string = '', maxLength = 140) =>
|
||||||
|
string.length > maxLength
|
||||||
|
? `${string.substring(0, maxLength)} […]`
|
||||||
|
: string
|
||||||
|
|
||||||
export default function Comment ({
|
export default function Comment ({
|
||||||
item, children, replyOpen, includeParent,
|
item, children, replyOpen, includeParent,
|
||||||
rootText, noComments, noReply
|
rootText, noComments, noReply, truncate
|
||||||
}) {
|
}) {
|
||||||
const [edit, setEdit] = useState()
|
const [edit, setEdit] = useState()
|
||||||
const [collapse, setCollapse] = useState(false)
|
const [collapse, setCollapse] = useState(false)
|
||||||
@ -129,7 +134,9 @@ export default function Comment ({
|
|||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<div className={styles.text}>
|
<div className={styles.text}>
|
||||||
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text>
|
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>
|
||||||
|
{truncate ? truncateString(item.text) : item.text}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -132,7 +132,7 @@ export default function UserHeader ({ user }) {
|
|||||||
</Nav.Item>
|
</Nav.Item>
|
||||||
{isMe &&
|
{isMe &&
|
||||||
<Nav.Item>
|
<Nav.Item>
|
||||||
<Link href='/satistics?inc=invoice,withdrawal,stacked,spent' passHref>
|
<Link href='/satistics?inc=invoice,withdrawal' passHref>
|
||||||
<Nav.Link eventKey='/satistics'>satistics</Nav.Link>
|
<Nav.Link eventKey='/satistics'>satistics</Nav.Link>
|
||||||
</Link>
|
</Link>
|
||||||
</Nav.Item>}
|
</Nav.Item>}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { gql } from '@apollo/client'
|
import { gql } from '@apollo/client'
|
||||||
|
import { ITEM_FIELDS } from './items'
|
||||||
|
|
||||||
export const INVOICE = gql`
|
export const INVOICE = gql`
|
||||||
query Invoice($id: ID!) {
|
query Invoice($id: ID!) {
|
||||||
@ -25,6 +26,8 @@ export const WITHDRAWL = gql`
|
|||||||
}`
|
}`
|
||||||
|
|
||||||
export const WALLET_HISTORY = gql`
|
export const WALLET_HISTORY = gql`
|
||||||
|
${ITEM_FIELDS}
|
||||||
|
|
||||||
query WalletHistory($cursor: String, $inc: String) {
|
query WalletHistory($cursor: String, $inc: String) {
|
||||||
walletHistory(cursor: $cursor, inc: $inc) {
|
walletHistory(cursor: $cursor, inc: $inc) {
|
||||||
facts {
|
facts {
|
||||||
@ -37,6 +40,10 @@ export const WALLET_HISTORY = gql`
|
|||||||
status
|
status
|
||||||
type
|
type
|
||||||
description
|
description
|
||||||
|
item {
|
||||||
|
...ItemFields
|
||||||
|
text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cursor
|
cursor
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Table } from 'react-bootstrap'
|
import { Table } from 'react-bootstrap'
|
||||||
import useDarkMode from 'use-dark-mode'
|
|
||||||
import { getGetServerSideProps } from '../api/ssrApollo'
|
import { getGetServerSideProps } from '../api/ssrApollo'
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
import { useMe } from '../components/me'
|
import { useMe } from '../components/me'
|
||||||
@ -14,10 +13,16 @@ import Check from '../svgs/check-double-line.svg'
|
|||||||
import ThumbDown from '../svgs/thumb-down-fill.svg'
|
import ThumbDown from '../svgs/thumb-down-fill.svg'
|
||||||
import { Checkbox, Form } from '../components/form'
|
import { Checkbox, Form } from '../components/form'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import Item from '../components/item'
|
||||||
|
import Comment from '../components/comment'
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps(WALLET_HISTORY)
|
export const getServerSideProps = getGetServerSideProps(WALLET_HISTORY)
|
||||||
|
|
||||||
function satusClass (status) {
|
function satusClass (status) {
|
||||||
|
if (!status) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'CONFIRMED':
|
case 'CONFIRMED':
|
||||||
return ''
|
return ''
|
||||||
@ -82,14 +87,30 @@ function Satus ({ status }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Detail ({ fact }) {
|
||||||
|
if (!fact.item) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={satusClass(fact.status)}>
|
||||||
|
{fact.description || 'no description'}
|
||||||
|
</div>
|
||||||
|
<Satus status={fact.status} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fact.item.title) {
|
||||||
|
return <div className={styles.itemWrapper}><Item item={fact.item} /></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className={styles.commentWrapper}><Comment item={fact.item} includeParent noReply truncate /></div>
|
||||||
|
}
|
||||||
|
|
||||||
export default function Satistics ({ data: { walletHistory: { facts, cursor } } }) {
|
export default function Satistics ({ data: { walletHistory: { facts, cursor } } }) {
|
||||||
const me = useMe()
|
const me = useMe()
|
||||||
const { value: darkMode } = useDarkMode()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { data, fetchMore } = useQuery(WALLET_HISTORY, { variables: { inc: router.query.inc } })
|
const { data, fetchMore } = useQuery(WALLET_HISTORY, { variables: { inc: router.query.inc } })
|
||||||
|
|
||||||
console.log(router.query.inc, data)
|
|
||||||
|
|
||||||
function filterRoutePush (filter, add) {
|
function filterRoutePush (filter, add) {
|
||||||
const inc = new Set(router.query.inc.split(','))
|
const inc = new Set(router.query.inc.split(','))
|
||||||
inc.delete('')
|
inc.delete('')
|
||||||
@ -109,6 +130,16 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
|
|||||||
return inc.has(filter)
|
return inc.has(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function href (fact) {
|
||||||
|
switch (fact.type) {
|
||||||
|
case 'withdrawal':
|
||||||
|
case 'invoice':
|
||||||
|
return `/${fact.type}s/${fact.factId}`
|
||||||
|
default:
|
||||||
|
return `/items/${fact.factId}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
({ walletHistory: { facts, cursor } } = data)
|
({ walletHistory: { facts, cursor } } = data)
|
||||||
}
|
}
|
||||||
@ -149,7 +180,7 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
<Table className='mt-3 mb-0' bordered hover size='sm' variant={darkMode ? 'dark' : undefined}>
|
<Table className='mt-3 mb-0' bordered hover size='sm'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className={styles.type}>type</th>
|
<th className={styles.type}>type</th>
|
||||||
@ -159,14 +190,11 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{facts.map((f, i) => (
|
{facts.map((f, i) => (
|
||||||
<Link href={`${f.type}s/${f.factId}`} key={f.id}>
|
<Link href={href(f)} key={f.id}>
|
||||||
<tr className={styles.row}>
|
<tr className={styles.row}>
|
||||||
<td className={`${styles.type} ${satusClass(f.status)}`}>{f.type}</td>
|
<td className={`${styles.type} ${satusClass(f.status)}`}>{f.type}</td>
|
||||||
<td className={styles.description}>
|
<td className={styles.description}>
|
||||||
<div className={satusClass(f.status)}>
|
<Detail fact={f} />
|
||||||
{f.description || 'no description'}
|
|
||||||
</div>
|
|
||||||
<Satus status={f.status} />
|
|
||||||
</td>
|
</td>
|
||||||
<td className={`${styles.sats} ${satusClass(f.status)}`}>{f.msats / 1000}</td>
|
<td className={`${styles.sats} ${satusClass(f.status)}`}>{f.msats / 1000}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -59,8 +59,13 @@ $tooltip-bg: #5c8001;
|
|||||||
src: url(/Lightningvolt-xoqm.ttf);
|
src: url(/Lightningvolt-xoqm.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 767px) {
|
||||||
|
.table-sm th, .table-sm td {
|
||||||
|
padding: .3rem .75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.table-sm th, .table-sm td {
|
.table-sm th, .table-sm td {
|
||||||
padding: .3rem .75rem;
|
|
||||||
line-height: 1.2rem;
|
line-height: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +82,11 @@ $tooltip-bg: #5c8001;
|
|||||||
border-color: var(--theme-borderColor);
|
border-color: var(--theme-borderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-hover tbody tr:hover {
|
||||||
|
color: var(--theme-color);
|
||||||
|
background-color: var(--theme-clickToContextColor);
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--theme-body);
|
background: var(--theme-body);
|
||||||
color: var(--theme-color);
|
color: var(--theme-color);
|
||||||
|
@ -16,3 +16,13 @@
|
|||||||
.row {
|
.row {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.itemWrapper {
|
||||||
|
padding-top: .25rem;
|
||||||
|
margin-bottom: -.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentWrapper {
|
||||||
|
padding-top: .2rem;
|
||||||
|
padding-bottom: .4rem;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user