Skip to content

Commit

Permalink
fix: payment pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
apotdevin committed Apr 10, 2021
1 parent 292c6d9 commit 53739fa
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 165 deletions.
31 changes: 30 additions & 1 deletion config/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,36 @@ function createApolloClient(context?: ResolverContext) {
credentials: 'same-origin',
ssrMode: typeof window === 'undefined',
link: createIsomorphLink(context),
cache: new InMemoryCache(possibleTypes),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
getResume: {
keyArgs: [],
merge(existing, incoming, { args }) {
if (!existing) {
return incoming;
}

const { offset } = args || {};

const merged = existing?.resume ? existing.resume.slice(0) : [];
for (let i = 0; i < incoming.resume.length; ++i) {
merged[offset + i] = incoming.resume[i];
}

return {
...existing,
offset: incoming.offset,
resume: merged,
};
},
},
},
},
},
...possibleTypes,
}),
defaultOptions: {
query: {
fetchPolicy: 'cache-first',
Expand Down
78 changes: 40 additions & 38 deletions pages/transactions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify';
import { InvoiceCard } from 'src/views/transactions/InvoiceCard';
import {
useGetResumeQuery,
GetResumeQuery,
} from 'src/graphql/queries/__generated__/getResume.generated';
import { useGetResumeQuery } from 'src/graphql/queries/__generated__/getResume.generated';
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';

import { NextPageContext } from 'next';
Expand Down Expand Up @@ -47,36 +44,46 @@ const Rotation = styled.div<RotationProps>`
const TransactionsView = () => {
const [isPolling, setIsPolling] = useState(false);
const [indexOpen, setIndexOpen] = useState(0);
const [token, setToken] = useState('');

const [offset, setOffset] = useState(0);

const {
loading,
data,
fetchMore,
startPolling,
stopPolling,
networkStatus,
} = useGetResumeQuery({
ssr: false,
variables: { token: '' },
variables: { offset: 0 },
notifyOnNetworkStatusChange: true,
onError: error => toast.error(getErrorContent(error)),
});

const isLoading = networkStatus === 1;
const isRefetching = networkStatus === 3;

const loadingOrRefetching = isLoading || isRefetching;

useEffect(() => {
if (!loading && data && data.getResume && data.getResume.token) {
setToken(data.getResume.token);
if (!isLoading && data?.getResume?.offset) {
setOffset(data.getResume.offset);
}
}, [data, loading]);
}, [data, isLoading]);

useEffect(() => {
return () => stopPolling();
}, [stopPolling]);

if (loading || !data || !data.getResume) {
if (isLoading || !data || !data.getResume) {
return <LoadingCard title={'Transactions'} />;
}

const resumeList = data.getResume.resume;

const handleClick = (limit: number) =>
fetchMore({ variables: { offset, limit } });

return (
<>
<FlowBox />
Expand Down Expand Up @@ -130,36 +137,31 @@ const TransactionsView = () => {
return null;
})}
<ColorButton
loading={loadingOrRefetching}
disabled={loadingOrRefetching}
fullWidth={true}
withMargin={'16px 0 0'}
onClick={() => {
fetchMore({
variables: { token },
updateQuery: (
prev,
{ fetchMoreResult }: { fetchMoreResult?: GetResumeQuery }
): GetResumeQuery => {
if (!fetchMoreResult?.getResume) return prev;
const newToken = fetchMoreResult.getResume.token || '';
const prevEntries = prev?.getResume?.resume || [];
const newEntries = fetchMoreResult?.getResume?.resume || [];

const allTransactions = newToken
? [...prevEntries, ...newEntries]
: prevEntries;

return {
getResume: {
token: newToken,
resume: allTransactions,
__typename: 'getResumeType',
},
};
},
});
}}
onClick={() => handleClick(1)}
>
Get 1 More Day
</ColorButton>
<ColorButton
loading={loadingOrRefetching}
disabled={loadingOrRefetching}
fullWidth={true}
withMargin={'16px 0 0'}
onClick={() => handleClick(7)}
>
Get 1 More Week
</ColorButton>
<ColorButton
loading={loadingOrRefetching}
disabled={loadingOrRefetching}
fullWidth={true}
withMargin={'16px 0 0'}
onClick={() => handleClick(30)}
>
Show More
Get 1 More Month
</ColorButton>
</Card>
</CardWithTitle>
Expand Down
172 changes: 170 additions & 2 deletions server/schema/transactions/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { getChannel, getNode } from 'ln-service';
import { toWithError } from 'server/helpers/async';
import { compareDesc } from 'date-fns';
import { getChannel, getNode, getPayments, getInvoices } from 'ln-service';
import { to, toWithError } from 'server/helpers/async';
import { logger } from 'server/helpers/logger';
import {
ChannelType,
GetChannelType,
GetInvoicesType,
GetNodeType,
GetPaymentsType,
LndObject,
} from 'server/types/ln-service.types';

export const getNodeFromChannel = async (
Expand Down Expand Up @@ -85,3 +89,167 @@ export const getNodeFromChannel = async (
channel_id: channelId,
};
};

export const getPaymentsBetweenDates = async ({
lnd,
from,
until,
batch = 25,
}: {
lnd: LndObject | null;
from?: string;
until?: string;
batch?: number;
}) => {
const paymentList = await to<GetPaymentsType>(
getPayments({
lnd,
limit: batch,
})
);

if (!paymentList?.payments?.length) {
return [];
}

if (!from || !until) {
return paymentList.payments;
}

const firstPayment = paymentList.payments[0];

const isOutOf =
compareDesc(new Date(firstPayment.created_at), new Date(until)) === 1;

const filterArray = (payment: GetPaymentsType['payments'][0]) => {
const date = payment.created_at;
const last = compareDesc(new Date(until), new Date(date)) === 1;
const first = compareDesc(new Date(date), new Date(from)) === 1;

return last && first;
};

if (isOutOf) {
return paymentList.payments.filter(filterArray);
}

let completePayments = paymentList.payments;
let nextToken = paymentList.next;

let finished = false;

while (!finished) {
const newPayments = await to<GetPaymentsType>(
getPayments({
lnd,
token: nextToken,
})
);

if (!newPayments?.payments?.length) {
finished = true;
break;
}

completePayments = [...completePayments, ...newPayments.payments];

const firstPayment = newPayments.payments[0];

if (compareDesc(new Date(firstPayment.created_at), new Date(until)) === 1) {
finished = true;
break;
}

if (!newPayments.next) {
finished = true;
break;
}

nextToken = newPayments.next;
}

return completePayments.filter(filterArray);
};

export const getInvoicesBetweenDates = async ({
lnd,
from,
until,
batch = 25,
}: {
lnd: LndObject | null;
from?: string;
until?: string;
batch?: number;
}) => {
const invoiceList = await to<GetInvoicesType>(
getInvoices({
lnd,
limit: batch,
})
);

if (!invoiceList?.invoices?.length) {
return [];
}

if (!from || !until) {
return invoiceList.invoices;
}

const firstInvoice = invoiceList.invoices[0];
const firstDate = firstInvoice.confirmed_at || firstInvoice.created_at;

const isOutOf = compareDesc(new Date(firstDate), new Date(until)) === 1;

const filterArray = (invoice: GetInvoicesType['invoices'][0]) => {
const date = invoice.confirmed_at || invoice.created_at;
const last = compareDesc(new Date(until), new Date(date)) === 1;
const first = compareDesc(new Date(date), new Date(from)) === 1;

return last && first;
};

if (isOutOf) {
return invoiceList.invoices.filter(filterArray);
}

let completeInvoices = invoiceList.invoices;
let nextToken = invoiceList.next;

let finished = false;

while (!finished) {
const newInvoices = await to<GetInvoicesType>(
getInvoices({
lnd,
token: nextToken,
})
);

if (!newInvoices?.invoices?.length) {
finished = true;
break;
}

completeInvoices = [...completeInvoices, ...newInvoices.invoices];

const firstNewInvoice = newInvoices.invoices[0];
const firstNewDate =
firstNewInvoice.confirmed_at || firstNewInvoice.created_at;

if (compareDesc(new Date(firstNewDate), new Date(until)) === 1) {
finished = true;
break;
}

if (!newInvoices.next) {
finished = true;
break;
}

nextToken = newInvoices.next;
}

return completeInvoices.filter(filterArray);
};
Loading

0 comments on commit 53739fa

Please sign in to comment.