Paginate received payments (#1234)

* Add pagination to get received payments rpc

* Add pagination to received payments frontend page

* Comment unused method used for streaming in timeline page

* Run frontend lint

* Update frontend build
This commit is contained in:
Jonathan Zernik 2021-09-08 17:54:49 -07:00 committed by GitHub
parent b9dad8ce0e
commit b2858d714b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 169 additions and 48 deletions

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import {
Grid,
Button,
@ -6,12 +6,14 @@ import {
Tab,
AppBar,
Box,
CircularProgress,
} from '@material-ui/core';
// styles
import { makeStyles } from '@material-ui/core/styles';
// components
import ReplayIcon from '@material-ui/icons/Replay';
import Widget from '../../components/Widget';
import ReceivedPayment from '../../components/ReceivedPayment';
@ -30,10 +32,13 @@ const useStyles = makeStyles((theme) => ({
},
}));
const RECEIVED_PAYMENTS_PER_PAGE = 10;
export default function ReceivedPayments() {
const classes = useStyles();
const [value, setValue] = useState(0);
const [receivedPayments, setReceivedPayments] = useState([]);
const [waitingForReceivedPayments, setWaitingForReceivedPayments] = React.useState(false);
function a11yProps(index) {
return {
@ -46,9 +51,20 @@ export default function ReceivedPayments() {
setValue(newValue);
};
const loadReceivedPayments = () => {
getReceivedPaymentsRequest((receivedPaymentsReply) => {
setReceivedPayments(receivedPaymentsReply.getReceivedPaymentsList());
const loadReceivedPayments = useCallback((limit, lastReceivedPayment) => {
setWaitingForReceivedPayments(true);
getReceivedPaymentsRequest(limit, lastReceivedPayment, handleLoadedReceivedPayments);
},
[]);
const handleLoadedReceivedPayments = (reply) => {
const loadedReceivedPayments = reply.getReceivedPaymentsList();
setWaitingForReceivedPayments(false);
setReceivedPayments((prevReceivedPayments) => {
if (!prevReceivedPayments) {
return loadedReceivedPayments;
}
return prevReceivedPayments.concat(loadedReceivedPayments);
});
};
@ -59,8 +75,8 @@ export default function ReceivedPayments() {
};
useEffect(() => {
loadReceivedPayments();
}, []);
loadReceivedPayments(RECEIVED_PAYMENTS_PER_PAGE, null);
}, [loadReceivedPayments]);
function TabPanel(props) {
const {
@ -118,7 +134,6 @@ export default function ReceivedPayments() {
}
function ReceivedPaymentsContent() {
console.log(`receivedPayments: ${receivedPayments}`);
return (
<>
<Grid container spacing={4}>
@ -153,10 +168,38 @@ export default function ReceivedPayments() {
{PaymentsTabs()}
</Grid>
<Grid item xs={12} sm={3} />
{ViewMoreReceivedPaymentsButton()}
</Grid>
);
}
function ViewMoreReceivedPaymentsButton() {
return (
<>
<Grid item xs={12}>
<div className={classes.wrapper}>
{!waitingForReceivedPayments
&& (
<Button
variant="contained"
color="primary"
disabled={waitingForReceivedPayments}
onClick={() => {
const latestReceivedPayment = receivedPayments.slice(-1).pop();
loadReceivedPayments(RECEIVED_PAYMENTS_PER_PAGE, latestReceivedPayment);
}}
>
<ReplayIcon />
View more squeaks
</Button>
)}
{waitingForReceivedPayments && <CircularProgress size={48} className={classes.buttonProgress} />}
</div>
</Grid>
</>
);
}
return (
<>
{GridContent()}

View file

@ -80,14 +80,14 @@ export default function TimelinePage() {
});
};
const handleLoadedNewSqueak = (newSqueak) => {
setNewSqueaks((prevNewSqueaks) => {
if (!prevNewSqueaks) {
return [newSqueak];
}
return prevNewSqueaks.concat(newSqueak);
});
};
// const handleLoadedNewSqueak = (newSqueak) => {
// setNewSqueaks((prevNewSqueaks) => {
// if (!prevNewSqueaks) {
// return [newSqueak];
// }
// return prevNewSqueaks.concat(newSqueak);
// });
// };
useEffect(() => {
getSqueaks(SQUEAKS_PER_PAGE, null);

View file

@ -899,8 +899,10 @@ export function getSentPaymentsRequest(limit, lastSentPayment, handleResponse) {
// });
}
export function getReceivedPaymentsRequest(handleResponse) {
export function getReceivedPaymentsRequest(limit, lastReceivedPayment, handleResponse) {
const request = new GetReceivedPaymentsRequest();
request.setLimit(limit);
request.setLastReceivedPayment(lastReceivedPayment);
makeRequest(
'getreceivedpayments',
request,

View file

@ -664,7 +664,9 @@ def test_buy_squeak(
# Get the received payment from the seller node
get_received_payments_response = admin_stub.GetReceivedPayments(
squeak_admin_pb2.GetReceivedPaymentsRequest(),
squeak_admin_pb2.GetReceivedPaymentsRequest(
limit=100,
),
)
# print(
# "get_received_payments_response: {}".format(

View file

@ -927,6 +927,11 @@ message SentOffer {
}
message GetReceivedPaymentsRequest {
/// Limit number of results
int32 limit = 1;
/// Last entry
ReceivedPayment last_received_payment = 2;
}
message GetReceivedPaymentsReply {

View file

@ -234,3 +234,15 @@ def message_to_sent_payment(sent_payment: squeak_admin_pb2.SentPayment) -> SentP
node_pubkey=sent_payment.node_pubkey,
valid=sent_payment.valid,
)
def message_to_received_payment(received_payment: squeak_admin_pb2.ReceivedPayment) -> ReceivedPayment:
return ReceivedPayment(
received_payment_id=received_payment.received_payment_id,
created_time_ms=received_payment.time_ms,
squeak_hash=bytes.fromhex(received_payment.squeak_hash),
payment_hash=bytes.fromhex(received_payment.payment_hash),
price_msat=received_payment.price_msat,
settle_index=0, # TODO: This is not correct, fix later.
peer_address=message_to_peer_address(received_payment.peer_address),
)

View file

@ -24,6 +24,7 @@ import logging
from proto import squeak_admin_pb2
from squeaknode.admin.messages import connected_peer_to_message
from squeaknode.admin.messages import message_to_peer_address
from squeaknode.admin.messages import message_to_received_payment
from squeaknode.admin.messages import message_to_sent_payment
from squeaknode.admin.messages import message_to_squeak_entry
from squeaknode.admin.messages import offer_entry_to_message
@ -698,8 +699,25 @@ class SqueakAdminServerHandler(object):
)
def handle_get_received_payments(self, request):
logger.info("Handle get received payments")
received_payments = self.squeak_controller.get_received_payments()
limit = request.limit
last_received_payment = message_to_received_payment(request.last_received_payment) if request.HasField(
"last_received_payment") else None
logger.info("""Handle get received payments with
limit: {}
last_received_payment: {}
""".format(
limit,
last_received_payment,
))
received_payments = self.squeak_controller.get_received_payments(
limit,
last_received_payment,
)
logger.info(
"Got number of received payments: {}".format(
len(received_payments)
)
)
received_payment_msgs = [
received_payments_to_message(received_payment)
for received_payment in received_payments

View file

@ -1,17 +1,17 @@
{
"files": {
"main.js": "/static/js/main.79916aed.chunk.js",
"main.js.map": "/static/js/main.79916aed.chunk.js.map",
"main.js": "/static/js/main.b5dd81a1.chunk.js",
"main.js.map": "/static/js/main.b5dd81a1.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.9f0ba400.js",
"runtime-main.js.map": "/static/js/runtime-main.9f0ba400.js.map",
"static/css/2.ea4ba2f0.chunk.css": "/static/css/2.ea4ba2f0.chunk.css",
"static/js/2.e1ce3d4b.chunk.js": "/static/js/2.e1ce3d4b.chunk.js",
"static/js/2.e1ce3d4b.chunk.js.map": "/static/js/2.e1ce3d4b.chunk.js.map",
"static/js/2.4181937e.chunk.js": "/static/js/2.4181937e.chunk.js",
"static/js/2.4181937e.chunk.js.map": "/static/js/2.4181937e.chunk.js.map",
"index.html": "/index.html",
"precache-manifest.aefedce4f358985ca90741faf5431bc5.js": "/precache-manifest.aefedce4f358985ca90741faf5431bc5.js",
"precache-manifest.8a0c063712e93bdb27b968958cbb7b2b.js": "/precache-manifest.8a0c063712e93bdb27b968958cbb7b2b.js",
"service-worker.js": "/service-worker.js",
"static/css/2.ea4ba2f0.chunk.css.map": "/static/css/2.ea4ba2f0.chunk.css.map",
"static/js/2.e1ce3d4b.chunk.js.LICENSE.txt": "/static/js/2.e1ce3d4b.chunk.js.LICENSE.txt",
"static/js/2.4181937e.chunk.js.LICENSE.txt": "/static/js/2.4181937e.chunk.js.LICENSE.txt",
"static/media/font-awesome.min.css": "/static/media/fontawesome-webfont.fee66e71.woff",
"static/media/google.svg": "/static/media/google.695a3160.svg",
"static/media/logo.svg": "/static/media/logo.a0185b04.svg"
@ -19,7 +19,7 @@
"entrypoints": [
"static/js/runtime-main.9f0ba400.js",
"static/css/2.ea4ba2f0.chunk.css",
"static/js/2.e1ce3d4b.chunk.js",
"static/js/main.79916aed.chunk.js"
"static/js/2.4181937e.chunk.js",
"static/js/main.b5dd81a1.chunk.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Squeak Node</title><meta name="description" content="Squeak Node is a frontend for accessing a squeak node"><meta name="keywords" content="squeak, bitcoin, lightning"><meta name="author" content="Flatlogic LLC."><link href="/static/css/2.ea4ba2f0.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,l=r[0],a=r[1],i=r[2],c=0,s=[];c<l.length;c++)f=l[c],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var a=t[l];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var l=this["webpackJsonpsqueak-node-frontend"]=this["webpackJsonpsqueak-node-frontend"]||[],a=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var p=a;t()}([])</script><script src="/static/js/2.e1ce3d4b.chunk.js"></script><script src="/static/js/main.79916aed.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Squeak Node</title><meta name="description" content="Squeak Node is a frontend for accessing a squeak node"><meta name="keywords" content="squeak, bitcoin, lightning"><meta name="author" content="Flatlogic LLC."><link href="/static/css/2.ea4ba2f0.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,l=r[0],a=r[1],i=r[2],c=0,s=[];c<l.length;c++)f=l[c],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var a=t[l];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var l=this["webpackJsonpsqueak-node-frontend"]=this["webpackJsonpsqueak-node-frontend"]||[],a=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var p=a;t()}([])</script><script src="/static/js/2.4181937e.chunk.js"></script><script src="/static/js/main.b5dd81a1.chunk.js"></script></body></html>

View file

@ -1,23 +1,23 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "c902aa1bcd9c91e45badf061208b8637",
"revision": "47dabe8d9a77cca2c8d5407e18931825",
"url": "/index.html"
},
{
"revision": "785caac4a07b948d3be4",
"revision": "f1e946917c419318e8ac",
"url": "/static/css/2.ea4ba2f0.chunk.css"
},
{
"revision": "785caac4a07b948d3be4",
"url": "/static/js/2.e1ce3d4b.chunk.js"
"revision": "f1e946917c419318e8ac",
"url": "/static/js/2.4181937e.chunk.js"
},
{
"revision": "279b8724b89bf7d504758b3fa917239f",
"url": "/static/js/2.e1ce3d4b.chunk.js.LICENSE.txt"
"url": "/static/js/2.4181937e.chunk.js.LICENSE.txt"
},
{
"revision": "79092192931dd8817290",
"url": "/static/js/main.79916aed.chunk.js"
"revision": "519826351c7336fbd239",
"url": "/static/js/main.b5dd81a1.chunk.js"
},
{
"revision": "cc9816de5a8639d377ea",

View file

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.aefedce4f358985ca90741faf5431bc5.js"
"/precache-manifest.8a0c063712e93bdb27b968958cbb7b2b.js"
);
self.addEventListener('message', (event) => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1221,10 +1221,41 @@ class SqueakDb:
except sqlalchemy.exc.IntegrityError:
raise DuplicateReceivedPaymentError()
def get_received_payments(self) -> List[ReceivedPayment]:
def get_received_payments(
self,
limit: int,
last_received_payment: Optional[ReceivedPayment],
) -> List[ReceivedPayment]:
""" Get all received payments. """
s = select([self.received_payments]).order_by(
self.received_payments.c.created_time_ms.desc(),
last_created_time = last_received_payment.created_time_ms if last_received_payment else self.timestamp_now_ms
last_payment_hash = last_received_payment.payment_hash if last_received_payment else MAX_HASH
logger.info("""Get received payments db query with
limit: {}
last_created_time: {}
last_payment_hash: {}
""".format(
limit,
last_created_time,
last_payment_hash.hex(),
))
s = (
select([self.received_payments])
.order_by(
)
.where(
tuple_(
self.received_payments.c.created_time_ms,
self.received_payments.c.payment_hash,
) < tuple_(
last_created_time,
last_payment_hash,
)
)
.order_by(
self.received_payments.c.created_time_ms.desc(),
self.received_payments.c.payment_hash.desc(),
)
.limit(limit)
)
with self.get_connection() as connection:
result = connection.execute(s)

View file

@ -42,6 +42,7 @@ from squeaknode.core.block_range import BlockRange
from squeaknode.core.offer import Offer
from squeaknode.core.peer_address import PeerAddress
from squeaknode.core.received_offer import ReceivedOffer
from squeaknode.core.received_payment import ReceivedPayment
from squeaknode.core.received_payment_summary import ReceivedPaymentSummary
from squeaknode.core.sent_offer import SentOffer
from squeaknode.core.sent_payment import SentPayment
@ -431,8 +432,15 @@ class SqueakController:
def get_sent_offers(self):
return self.squeak_db.get_sent_offers()
def get_received_payments(self):
return self.squeak_db.get_received_payments()
def get_received_payments(
self,
limit: int,
last_received_payment: Optional[ReceivedPayment],
) -> List[ReceivedPayment]:
return self.squeak_db.get_received_payments(
limit,
last_received_payment,
)
def delete_all_expired_received_offers(self):
num_expired_received_offers = self.squeak_db.delete_expired_received_offers()