mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 14:22:33 +01:00
Merge pull request #236 from apotdevin/fix/payment-pagination
fix: payment pagination
This commit is contained in:
commit
b30aee8b6b
11 changed files with 296 additions and 165 deletions
|
@ -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',
|
||||
|
|
|
@ -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';
|
||||
|
@ -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 />
|
||||
|
@ -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)}
|
||||
>
|
||||
Show More
|
||||
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)}
|
||||
>
|
||||
Get 1 More Month
|
||||
</ColorButton>
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
|
|
|
@ -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 (
|
||||
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -1,68 +1,40 @@
|
|||
import { getPayments, getInvoices } from 'ln-service';
|
||||
import { compareDesc } from 'date-fns';
|
||||
import { subDays } from 'date-fns';
|
||||
import { sortBy } from 'underscore';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { to } from 'server/helpers/async';
|
||||
import {
|
||||
GetInvoicesType,
|
||||
GetPaymentsType,
|
||||
InvoiceType,
|
||||
PaymentType,
|
||||
} from 'server/types/ln-service.types';
|
||||
import { InvoiceType, PaymentType } from 'server/types/ln-service.types';
|
||||
import { decodeMessages } from 'server/helpers/customRecords';
|
||||
import { getInvoicesBetweenDates, getPaymentsBetweenDates } from './helpers';
|
||||
|
||||
type TransactionType = InvoiceType | PaymentType;
|
||||
type TransactionWithType = { isTypeOf: string } & TransactionType;
|
||||
|
||||
export const transactionResolvers = {
|
||||
Query: {
|
||||
getResume: async (_: undefined, params: any, context: ContextType) => {
|
||||
getResume: async (
|
||||
_: undefined,
|
||||
{ offset, limit }: { offset?: number; limit?: number },
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'payments');
|
||||
|
||||
const { lnd } = context;
|
||||
|
||||
const invoiceProps = params.token
|
||||
? { token: params.token }
|
||||
: { limit: 25 };
|
||||
const start = offset || 0;
|
||||
const end = (offset || 0) + (limit || 7);
|
||||
|
||||
let lastInvoiceDate = '';
|
||||
let firstInvoiceDate = '';
|
||||
let token = '';
|
||||
let withInvoices = true;
|
||||
const today = new Date();
|
||||
const startDate = subDays(today, start).toISOString();
|
||||
const endDate = subDays(today, end).toISOString();
|
||||
|
||||
const invoiceList = await to<GetInvoicesType>(
|
||||
getInvoices({
|
||||
lnd,
|
||||
...invoiceProps,
|
||||
})
|
||||
);
|
||||
|
||||
const invoices = invoiceList.invoices.map(invoice => {
|
||||
return {
|
||||
type: 'invoice',
|
||||
date: invoice.confirmed_at || invoice.created_at,
|
||||
...invoice,
|
||||
isTypeOf: 'InvoiceType',
|
||||
};
|
||||
const payments = await getPaymentsBetweenDates({
|
||||
lnd,
|
||||
from: startDate,
|
||||
until: endDate,
|
||||
batch: 25,
|
||||
});
|
||||
|
||||
if (invoices.length <= 0) {
|
||||
withInvoices = false;
|
||||
} else {
|
||||
const { date } = invoices[invoices.length - 1];
|
||||
firstInvoiceDate = invoices[0].date;
|
||||
lastInvoiceDate = date;
|
||||
token = invoiceList.next || '';
|
||||
}
|
||||
|
||||
const paymentList = await to<GetPaymentsType>(
|
||||
getPayments({
|
||||
lnd,
|
||||
})
|
||||
);
|
||||
|
||||
const payments = paymentList.payments.map(payment => ({
|
||||
const mappedPayments = payments.map(payment => ({
|
||||
...payment,
|
||||
type: 'payment',
|
||||
date: payment.created_at,
|
||||
|
@ -71,34 +43,32 @@ export const transactionResolvers = {
|
|||
isTypeOf: 'PaymentType',
|
||||
}));
|
||||
|
||||
const filterArray = (payment: typeof payments[number]) => {
|
||||
const last =
|
||||
compareDesc(new Date(lastInvoiceDate), new Date(payment.date)) === 1;
|
||||
const first = params.token
|
||||
? compareDesc(new Date(payment.date), new Date(firstInvoiceDate)) ===
|
||||
1
|
||||
: true;
|
||||
return last && first;
|
||||
};
|
||||
const invoices = await getInvoicesBetweenDates({
|
||||
lnd,
|
||||
from: startDate,
|
||||
until: endDate,
|
||||
batch: 25,
|
||||
});
|
||||
|
||||
const filteredPayments = withInvoices
|
||||
? payments.filter(filterArray)
|
||||
: payments;
|
||||
|
||||
const invoicesWithMessages = invoices.map(i => ({
|
||||
...i,
|
||||
messages: i.payments
|
||||
.map(p => decodeMessages(p.messages))
|
||||
.filter(Boolean),
|
||||
}));
|
||||
const mappedInvoices = invoices.map(invoice => {
|
||||
return {
|
||||
type: 'invoice',
|
||||
date: invoice.confirmed_at || invoice.created_at,
|
||||
...invoice,
|
||||
isTypeOf: 'InvoiceType',
|
||||
messages: invoice.payments
|
||||
.map(p => decodeMessages(p.messages))
|
||||
.filter(Boolean),
|
||||
};
|
||||
});
|
||||
|
||||
const resume = sortBy(
|
||||
[...invoicesWithMessages, ...filteredPayments],
|
||||
[...mappedInvoices, ...mappedPayments],
|
||||
'date'
|
||||
).reverse();
|
||||
|
||||
return {
|
||||
token,
|
||||
offset: end,
|
||||
resume,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -72,7 +72,7 @@ export const transactionTypes = gql`
|
|||
union Transaction = InvoiceType | PaymentType
|
||||
|
||||
type getResumeType {
|
||||
token: String
|
||||
offset: Int
|
||||
resume: [Transaction]
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -62,7 +62,7 @@ export const queryTypes = gql`
|
|||
getNode(publicKey: String!, withoutChannels: Boolean): Node!
|
||||
decodeRequest(request: String!): decodeType
|
||||
getWalletInfo: walletInfoType
|
||||
getResume(token: String): getResumeType
|
||||
getResume(offset: Int, limit: Int): getResumeType
|
||||
getForwards(days: Int!): [Forward]!
|
||||
getBitcoinPrice(logger: Boolean, currency: String): String
|
||||
getBitcoinFees(logger: Boolean): bitcoinFeeType
|
||||
|
|
|
@ -5,7 +5,8 @@ import { gql } from '@apollo/client';
|
|||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {}
|
||||
export type GetResumeQueryVariables = Types.Exact<{
|
||||
token?: Types.Maybe<Types.Scalars['String']>;
|
||||
offset?: Types.Maybe<Types.Scalars['Int']>;
|
||||
limit?: Types.Maybe<Types.Scalars['Int']>;
|
||||
}>;
|
||||
|
||||
|
||||
|
@ -13,7 +14,7 @@ export type GetResumeQuery = (
|
|||
{ __typename?: 'Query' }
|
||||
& { getResume?: Types.Maybe<(
|
||||
{ __typename?: 'getResumeType' }
|
||||
& Pick<Types.GetResumeType, 'token'>
|
||||
& Pick<Types.GetResumeType, 'offset'>
|
||||
& { resume?: Types.Maybe<Array<Types.Maybe<(
|
||||
{ __typename?: 'InvoiceType' }
|
||||
& Pick<Types.InvoiceType, 'chain_address' | 'confirmed_at' | 'created_at' | 'description' | 'description_hash' | 'expires_at' | 'id' | 'is_canceled' | 'is_confirmed' | 'is_held' | 'is_private' | 'is_push' | 'received' | 'received_mtokens' | 'request' | 'secret' | 'tokens' | 'type' | 'date'>
|
||||
|
@ -43,9 +44,9 @@ export type GetResumeQuery = (
|
|||
|
||||
|
||||
export const GetResumeDocument = gql`
|
||||
query GetResume($token: String) {
|
||||
getResume(token: $token) {
|
||||
token
|
||||
query GetResume($offset: Int, $limit: Int) {
|
||||
getResume(offset: $offset, limit: $limit) {
|
||||
offset
|
||||
resume {
|
||||
... on InvoiceType {
|
||||
chain_address
|
||||
|
@ -117,7 +118,8 @@ export const GetResumeDocument = gql`
|
|||
* @example
|
||||
* const { data, loading, error } = useGetResumeQuery({
|
||||
* variables: {
|
||||
* token: // value for 'token'
|
||||
* offset: // value for 'offset'
|
||||
* limit: // value for 'limit'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
|
|
|
@ -931,47 +931,6 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`Query tests "GET_RESUME" matches snapshot 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"getResume": Object {
|
||||
"resume": Array [
|
||||
Object {
|
||||
"chain_address": "string",
|
||||
"confirmed_at": "string",
|
||||
"created_at": "string",
|
||||
"date": "string",
|
||||
"description": "string",
|
||||
"description_hash": "string",
|
||||
"expires_at": "string",
|
||||
"id": "string",
|
||||
"is_canceled": true,
|
||||
"is_confirmed": true,
|
||||
"is_held": true,
|
||||
"is_private": true,
|
||||
"is_push": true,
|
||||
"messages": Array [],
|
||||
"received": 1000,
|
||||
"received_mtokens": "string",
|
||||
"request": "string",
|
||||
"secret": "string",
|
||||
"tokens": "1000",
|
||||
"type": "invoice",
|
||||
},
|
||||
],
|
||||
"token": "string",
|
||||
},
|
||||
},
|
||||
"errors": undefined,
|
||||
"extensions": undefined,
|
||||
"http": Object {
|
||||
"headers": Headers {
|
||||
Symbol(map): Object {},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Query tests "GET_SERVER_ACCOUNTS" matches snapshot 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from '../getNodeInfo';
|
||||
import { GET_PEERS } from '../getPeers';
|
||||
import { GET_PENDING_CHANNELS } from '../getPendingChannels';
|
||||
import { GET_RESUME } from '../getResume';
|
||||
// import { GET_RESUME } from '../getResume';
|
||||
import { GET_SERVER_ACCOUNTS } from '../getServerAccounts';
|
||||
import { GET_TIME_HEALTH } from '../getTimeHealth';
|
||||
import { GET_UTXOS } from '../getUtxos';
|
||||
|
@ -69,7 +69,7 @@ const cases: CaseType[] = [
|
|||
['GET_CONNECT_INFO', { query: GET_CONNECT_INFO }],
|
||||
['GET_PEERS', { query: GET_PEERS }],
|
||||
['GET_PENDING_CHANNELS', { query: GET_PENDING_CHANNELS }],
|
||||
['GET_RESUME', { query: GET_RESUME }],
|
||||
// ['GET_RESUME', { query: GET_RESUME }],
|
||||
['GET_SERVER_ACCOUNTS', { query: GET_SERVER_ACCOUNTS }],
|
||||
['GET_TIME_HEALTH', { query: GET_TIME_HEALTH }],
|
||||
['GET_UTXOS', { query: GET_UTXOS }],
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_RESUME = gql`
|
||||
query GetResume($token: String) {
|
||||
getResume(token: $token) {
|
||||
token
|
||||
query GetResume($offset: Int, $limit: Int) {
|
||||
getResume(offset: $offset, limit: $limit) {
|
||||
offset
|
||||
resume {
|
||||
... on InvoiceType {
|
||||
chain_address
|
||||
|
|
|
@ -147,7 +147,8 @@ export type QueryDecodeRequestArgs = {
|
|||
|
||||
|
||||
export type QueryGetResumeArgs = {
|
||||
token?: Maybe<Scalars['String']>;
|
||||
offset?: Maybe<Scalars['Int']>;
|
||||
limit?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
|
@ -906,7 +907,7 @@ export type Transaction = InvoiceType | PaymentType;
|
|||
|
||||
export type GetResumeType = {
|
||||
__typename?: 'getResumeType';
|
||||
token?: Maybe<Scalars['String']>;
|
||||
offset?: Maybe<Scalars['Int']>;
|
||||
resume?: Maybe<Array<Maybe<Transaction>>>;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue