mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 14:22:33 +01:00
refactor: ♻️ forward channels
This commit is contained in:
parent
72860f334e
commit
05fd6b2573
10 changed files with 144 additions and 334 deletions
|
@ -1,10 +1,8 @@
|
||||||
import { getForwardChannelsReport } from './resolvers/getForwardChannelsReport';
|
|
||||||
import { getInOut } from './resolvers/getInOut';
|
import { getInOut } from './resolvers/getInOut';
|
||||||
import { getChannelReport } from './resolvers/getChannelReport';
|
import { getChannelReport } from './resolvers/getChannelReport';
|
||||||
|
|
||||||
export const widgetResolvers = {
|
export const widgetResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getForwardChannelsReport,
|
|
||||||
getInOut,
|
getInOut,
|
||||||
getChannelReport,
|
getChannelReport,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
import { getForwards, getWalletInfo, getClosedChannels } from 'ln-service';
|
|
||||||
import { subHours, subDays } from 'date-fns';
|
|
||||||
import { sortBy } from 'underscore';
|
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
|
||||||
import { getNodeFromChannel } from 'server/helpers/getNodeFromChannel';
|
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
|
||||||
import { to } from 'server/helpers/async';
|
|
||||||
import {
|
|
||||||
GetForwardsType,
|
|
||||||
GetWalletInfoType,
|
|
||||||
GetClosedChannelsType,
|
|
||||||
} from 'server/types/ln-service.types';
|
|
||||||
import { countArray, countRoutes } from './helpers';
|
|
||||||
|
|
||||||
export const getForwardChannelsReport = async (
|
|
||||||
_: undefined,
|
|
||||||
params: any,
|
|
||||||
context: ContextType
|
|
||||||
) => {
|
|
||||||
await requestLimiter(context.ip, 'forwardChannels');
|
|
||||||
|
|
||||||
const { lnd } = context;
|
|
||||||
|
|
||||||
let startDate = new Date();
|
|
||||||
const endDate = new Date();
|
|
||||||
|
|
||||||
if (params.time === 'week') {
|
|
||||||
startDate = subDays(endDate, 7);
|
|
||||||
} else if (params.time === 'month') {
|
|
||||||
startDate = subDays(endDate, 30);
|
|
||||||
} else if (params.time === 'quarter_year') {
|
|
||||||
startDate = subDays(endDate, 90);
|
|
||||||
} else if (params.time === 'half_year') {
|
|
||||||
startDate = subDays(endDate, 180);
|
|
||||||
} else if (params.time === 'year') {
|
|
||||||
startDate = subDays(endDate, 360);
|
|
||||||
} else {
|
|
||||||
startDate = subHours(endDate, 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
const closedChannels = await to<GetClosedChannelsType>(
|
|
||||||
getClosedChannels({
|
|
||||||
lnd,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const getRouteAlias = (array: any[], publicKey: string) =>
|
|
||||||
Promise.all(
|
|
||||||
array.map(async channel => {
|
|
||||||
const nodeAliasIn = await getNodeFromChannel(
|
|
||||||
channel.in,
|
|
||||||
publicKey,
|
|
||||||
lnd,
|
|
||||||
closedChannels?.channels.find(c => c.id === channel.in)
|
|
||||||
);
|
|
||||||
const nodeAliasOut = await getNodeFromChannel(
|
|
||||||
channel.out,
|
|
||||||
publicKey,
|
|
||||||
lnd,
|
|
||||||
closedChannels?.channels.find(c => c.id === channel.out)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
aliasIn: nodeAliasIn.alias,
|
|
||||||
colorIn: nodeAliasIn.color,
|
|
||||||
aliasOut: nodeAliasOut.alias,
|
|
||||||
colorOut: nodeAliasOut.color,
|
|
||||||
...channel,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const getAlias = (array: any[], publicKey: string) =>
|
|
||||||
Promise.all(
|
|
||||||
array.map(async channel => {
|
|
||||||
const nodeAlias = await getNodeFromChannel(
|
|
||||||
channel.name,
|
|
||||||
publicKey,
|
|
||||||
lnd,
|
|
||||||
closedChannels?.channels.find(c => c.id === channel.name)
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
alias: nodeAlias.alias,
|
|
||||||
color: nodeAlias.color,
|
|
||||||
...channel,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const forwardsList = await to<GetForwardsType>(
|
|
||||||
getForwards({
|
|
||||||
lnd,
|
|
||||||
after: startDate,
|
|
||||||
before: endDate,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const walletInfo = await to<GetWalletInfoType>(
|
|
||||||
getWalletInfo({
|
|
||||||
lnd,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let forwards = forwardsList.forwards;
|
|
||||||
let next = forwardsList.next;
|
|
||||||
|
|
||||||
let finishedFetching = false;
|
|
||||||
|
|
||||||
if (!next || !forwards || forwards.length <= 0) {
|
|
||||||
finishedFetching = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!finishedFetching) {
|
|
||||||
if (next) {
|
|
||||||
const moreForwards = await to<GetForwardsType>(
|
|
||||||
getForwards({ lnd, token: next })
|
|
||||||
);
|
|
||||||
forwards = [...forwards, ...moreForwards.forwards];
|
|
||||||
if (moreForwards.next) {
|
|
||||||
next = moreForwards.next;
|
|
||||||
} else {
|
|
||||||
finishedFetching = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
finishedFetching = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.type === 'route') {
|
|
||||||
const mapped = forwards.map(forward => {
|
|
||||||
return {
|
|
||||||
route: `${forward.incoming_channel} - ${forward.outgoing_channel}`,
|
|
||||||
...forward,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const grouped = countRoutes(mapped);
|
|
||||||
|
|
||||||
const routeAlias = await getRouteAlias(grouped, walletInfo.public_key);
|
|
||||||
|
|
||||||
const sortedRoute = sortBy(routeAlias, params.order).reverse().slice(0, 10);
|
|
||||||
return JSON.stringify(sortedRoute);
|
|
||||||
}
|
|
||||||
if (params.type === 'incoming') {
|
|
||||||
const incomingCount = countArray(forwards, true);
|
|
||||||
const incomingAlias = await getAlias(incomingCount, walletInfo.public_key);
|
|
||||||
const sortedInCount = sortBy(incomingAlias, params.order)
|
|
||||||
.reverse()
|
|
||||||
.slice(0, 10);
|
|
||||||
return JSON.stringify(sortedInCount);
|
|
||||||
}
|
|
||||||
const outgoingCount = countArray(forwards, false);
|
|
||||||
const outgoingAlias = await getAlias(outgoingCount, walletInfo.public_key);
|
|
||||||
const sortedOutCount = sortBy(outgoingAlias, params.order)
|
|
||||||
.reverse()
|
|
||||||
.slice(0, 10);
|
|
||||||
return JSON.stringify(sortedOutCount);
|
|
||||||
};
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { reduce, groupBy } from 'underscore';
|
|
||||||
import { ForwardType } from 'server/types/ln-service.types';
|
|
||||||
import { InOutProps, InOutListProps } from './interface';
|
import { InOutProps, InOutListProps } from './interface';
|
||||||
|
|
||||||
export const reduceInOutArray = (list: InOutListProps) => {
|
export const reduceInOutArray = (list: InOutListProps) => {
|
||||||
|
@ -7,8 +5,7 @@ export const reduceInOutArray = (list: InOutListProps) => {
|
||||||
for (const key in list) {
|
for (const key in list) {
|
||||||
if (Object.prototype.hasOwnProperty.call(list, key)) {
|
if (Object.prototype.hasOwnProperty.call(list, key)) {
|
||||||
const element: InOutProps[] = list[key];
|
const element: InOutProps[] = list[key];
|
||||||
const reducedArray: InOutProps = reduce(
|
const reducedArray: InOutProps = element.reduce(
|
||||||
element,
|
|
||||||
(a, b) => ({
|
(a, b) => ({
|
||||||
tokens: a.tokens + b.tokens,
|
tokens: a.tokens + b.tokens,
|
||||||
}),
|
}),
|
||||||
|
@ -23,62 +20,3 @@ export const reduceInOutArray = (list: InOutListProps) => {
|
||||||
}
|
}
|
||||||
return reducedOrder;
|
return reducedOrder;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const countArray = (list: ForwardType[], type: boolean) => {
|
|
||||||
const inOrOut = type ? 'incoming_channel' : 'outgoing_channel';
|
|
||||||
const grouped = groupBy(list, inOrOut);
|
|
||||||
|
|
||||||
const channelInfo = [];
|
|
||||||
for (const key in grouped) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(grouped, key)) {
|
|
||||||
const element = grouped[key];
|
|
||||||
|
|
||||||
const fee = element
|
|
||||||
.map(forward => forward.fee)
|
|
||||||
.reduce((p: number, c: number) => p + c);
|
|
||||||
|
|
||||||
const tokens = element
|
|
||||||
.map(forward => forward.tokens)
|
|
||||||
.reduce((p: number, c: number) => p + c);
|
|
||||||
|
|
||||||
channelInfo.push({
|
|
||||||
name: key,
|
|
||||||
amount: element.length,
|
|
||||||
fee,
|
|
||||||
tokens,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return channelInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const countRoutes = (list: ForwardType[]) => {
|
|
||||||
const grouped = groupBy(list, 'route');
|
|
||||||
|
|
||||||
const channelInfo = [];
|
|
||||||
for (const key in grouped) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(grouped, key)) {
|
|
||||||
const element = grouped[key];
|
|
||||||
|
|
||||||
const fee = element
|
|
||||||
.map(forward => forward.fee)
|
|
||||||
.reduce((p: number, c: number) => p + c);
|
|
||||||
|
|
||||||
const tokens = element
|
|
||||||
.map(forward => forward.tokens)
|
|
||||||
.reduce((p: number, c: number) => p + c);
|
|
||||||
|
|
||||||
channelInfo.push({
|
|
||||||
route: key,
|
|
||||||
in: element[0].incoming_channel,
|
|
||||||
out: element[0].outgoing_channel,
|
|
||||||
amount: element.length,
|
|
||||||
fee,
|
|
||||||
tokens,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return channelInfo;
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
import * as Types from '../../types';
|
|
||||||
|
|
||||||
import { gql } from '@apollo/client';
|
|
||||||
import * as Apollo from '@apollo/client';
|
|
||||||
export type GetForwardChannelsReportQueryVariables = Types.Exact<{
|
|
||||||
time?: Types.Maybe<Types.Scalars['String']>;
|
|
||||||
order?: Types.Maybe<Types.Scalars['String']>;
|
|
||||||
type?: Types.Maybe<Types.Scalars['String']>;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type GetForwardChannelsReportQuery = (
|
|
||||||
{ __typename?: 'Query' }
|
|
||||||
& Pick<Types.Query, 'getForwardChannelsReport'>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
export const GetForwardChannelsReportDocument = gql`
|
|
||||||
query GetForwardChannelsReport($time: String, $order: String, $type: String) {
|
|
||||||
getForwardChannelsReport(time: $time, order: $order, type: $type)
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useGetForwardChannelsReportQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useGetForwardChannelsReportQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useGetForwardChannelsReportQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
|
||||||
* you can use to render your UI.
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const { data, loading, error } = useGetForwardChannelsReportQuery({
|
|
||||||
* variables: {
|
|
||||||
* time: // value for 'time'
|
|
||||||
* order: // value for 'order'
|
|
||||||
* type: // value for 'type'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useGetForwardChannelsReportQuery(baseOptions?: Apollo.QueryHookOptions<GetForwardChannelsReportQuery, GetForwardChannelsReportQueryVariables>) {
|
|
||||||
return Apollo.useQuery<GetForwardChannelsReportQuery, GetForwardChannelsReportQueryVariables>(GetForwardChannelsReportDocument, baseOptions);
|
|
||||||
}
|
|
||||||
export function useGetForwardChannelsReportLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetForwardChannelsReportQuery, GetForwardChannelsReportQueryVariables>) {
|
|
||||||
return Apollo.useLazyQuery<GetForwardChannelsReportQuery, GetForwardChannelsReportQueryVariables>(GetForwardChannelsReportDocument, baseOptions);
|
|
||||||
}
|
|
||||||
export type GetForwardChannelsReportQueryHookResult = ReturnType<typeof useGetForwardChannelsReportQuery>;
|
|
||||||
export type GetForwardChannelsReportLazyQueryHookResult = ReturnType<typeof useGetForwardChannelsReportLazyQuery>;
|
|
||||||
export type GetForwardChannelsReportQueryResult = Apollo.QueryResult<GetForwardChannelsReportQuery, GetForwardChannelsReportQueryVariables>;
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { gql } from '@apollo/client';
|
|
||||||
|
|
||||||
export const GET_FORWARD_CHANNELS_REPORT = gql`
|
|
||||||
query GetForwardChannelsReport($time: String, $order: String, $type: String) {
|
|
||||||
getForwardChannelsReport(time: $time, order: $order, type: $type)
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -7,28 +7,23 @@ import { ResponsiveSingle } from 'src/components/generic/Styled';
|
||||||
import { ReportType, ReportDuration, FlowReportType } from './ForwardReport';
|
import { ReportType, ReportDuration, FlowReportType } from './ForwardReport';
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
isTime: ReportDuration;
|
days: number;
|
||||||
isType: ReportType;
|
order: ReportType;
|
||||||
setDays: (days: number) => void;
|
setDays: (days: number) => void;
|
||||||
setIsTime: (text: ReportDuration) => void;
|
setOrder: (text: ReportType) => void;
|
||||||
setIsType: (text: ReportType) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ButtonRow: React.FC<ButtonProps> = ({
|
export const ButtonRow: React.FC<ButtonProps> = ({
|
||||||
isTime,
|
days,
|
||||||
setIsTime,
|
|
||||||
setDays,
|
setDays,
|
||||||
isType,
|
order,
|
||||||
setIsType,
|
setOrder,
|
||||||
}) => {
|
}) => {
|
||||||
const timeButton = (time: ReportDuration, title: string, days: number) => (
|
const timeButton = (title: string, buttonDays: number) => (
|
||||||
<SingleButton
|
<SingleButton
|
||||||
withPadding={'4px 8px'}
|
withPadding={'4px 8px'}
|
||||||
onClick={() => {
|
onClick={() => setDays(buttonDays)}
|
||||||
setIsTime(time);
|
selected={days === buttonDays}
|
||||||
setDays(days);
|
|
||||||
}}
|
|
||||||
selected={isTime === time}
|
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</SingleButton>
|
</SingleButton>
|
||||||
|
@ -37,8 +32,8 @@ export const ButtonRow: React.FC<ButtonProps> = ({
|
||||||
const typeButton = (type: ReportType, title: string) => (
|
const typeButton = (type: ReportType, title: string) => (
|
||||||
<SingleButton
|
<SingleButton
|
||||||
withPadding={'4px 8px'}
|
withPadding={'4px 8px'}
|
||||||
onClick={() => setIsType(type)}
|
onClick={() => setOrder(type)}
|
||||||
selected={isType === type}
|
selected={order === type}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</SingleButton>
|
</SingleButton>
|
||||||
|
@ -47,12 +42,12 @@ export const ButtonRow: React.FC<ButtonProps> = ({
|
||||||
return (
|
return (
|
||||||
<ResponsiveSingle>
|
<ResponsiveSingle>
|
||||||
<MultiButton>
|
<MultiButton>
|
||||||
{timeButton('day', '1D', 1)}
|
{timeButton('1D', 1)}
|
||||||
{timeButton('week', '1W', 7)}
|
{timeButton('1W', 7)}
|
||||||
{timeButton('month', '1M', 30)}
|
{timeButton('1M', 30)}
|
||||||
{timeButton('quarter_year', '3M', 90)}
|
{timeButton('3M', 90)}
|
||||||
{timeButton('half_year', '6M', 180)}
|
{timeButton('6M', 180)}
|
||||||
{timeButton('year', '1Y', 360)}
|
{timeButton('1Y', 360)}
|
||||||
</MultiButton>
|
</MultiButton>
|
||||||
<MultiButton>
|
<MultiButton>
|
||||||
{typeButton('amount', 'Amount')}
|
{typeButton('amount', 'Amount')}
|
||||||
|
|
|
@ -2,11 +2,12 @@ import React, { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { GitCommit, ArrowDown, ArrowUp } from 'react-feather';
|
import { GitCommit, ArrowDown, ArrowUp } from 'react-feather';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useGetForwardChannelsReportQuery } from 'src/graphql/queries/__generated__/getForwardChannelsReport.generated';
|
|
||||||
import {
|
import {
|
||||||
MultiButton,
|
MultiButton,
|
||||||
SingleButton,
|
SingleButton,
|
||||||
} from 'src/components/buttons/multiButton/MultiButton';
|
} from 'src/components/buttons/multiButton/MultiButton';
|
||||||
|
import { useGetForwardsPastDaysQuery } from 'src/graphql/queries/__generated__/getForwardsPastDays.generated';
|
||||||
|
import { Forward } from 'src/graphql/types';
|
||||||
import { getErrorContent } from '../../../../utils/error';
|
import { getErrorContent } from '../../../../utils/error';
|
||||||
import {
|
import {
|
||||||
DarkSubTitle,
|
DarkSubTitle,
|
||||||
|
@ -16,7 +17,8 @@ import { LoadingCard } from '../../../../components/loading/LoadingCard';
|
||||||
import { getPrice } from '../../../../components/price/Price';
|
import { getPrice } from '../../../../components/price/Price';
|
||||||
import { useConfigState } from '../../../../context/ConfigContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import { usePriceState } from '../../../../context/PriceContext';
|
import { usePriceState } from '../../../../context/PriceContext';
|
||||||
import { ReportType, ReportDuration } from './ForwardReport';
|
import { ReportType } from './ForwardReport';
|
||||||
|
import { orderForwardChannels } from './helpers';
|
||||||
import { CardContent } from '.';
|
import { CardContent } from '.';
|
||||||
|
|
||||||
const ChannelRow = styled.div`
|
const ChannelRow = styled.div`
|
||||||
|
@ -39,11 +41,12 @@ const LastTableLine = styled(TableLine)`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isTime: ReportDuration;
|
days: number;
|
||||||
isType: ReportType;
|
order: ReportType;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ParsedRouteType = {
|
type ParsedRouteType = {
|
||||||
|
route: string;
|
||||||
aliasIn: string;
|
aliasIn: string;
|
||||||
aliasOut: string;
|
aliasOut: string;
|
||||||
fee: number;
|
fee: number;
|
||||||
|
@ -59,16 +62,16 @@ type ParsedChannelType = {
|
||||||
amount: number;
|
amount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ForwardChannelsReport = ({ isTime, isType }: Props) => {
|
export const ForwardChannelsReport = ({ days, order }: Props) => {
|
||||||
const [type, setType] = useState<'route' | 'incoming' | 'outgoing'>('route');
|
const [type, setType] = useState<'route' | 'incoming' | 'outgoing'>('route');
|
||||||
|
|
||||||
const { currency, displayValues } = useConfigState();
|
const { currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, displayValues, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const { data, loading } = useGetForwardChannelsReportQuery({
|
const { data, loading } = useGetForwardsPastDaysQuery({
|
||||||
ssr: false,
|
ssr: false,
|
||||||
variables: { time: isTime, order: isType, type },
|
variables: { days },
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,14 +79,15 @@ export const ForwardChannelsReport = ({ isTime, isType }: Props) => {
|
||||||
return <LoadingCard noCard={true} title={'Forward Report'} />;
|
return <LoadingCard noCard={true} title={'Forward Report'} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: JSON.parse is really bad... Absolutely no type safety at all
|
const forwardArray = orderForwardChannels(
|
||||||
const parsed: (ParsedChannelType | ParsedRouteType)[] = JSON.parse(
|
type,
|
||||||
data.getForwardChannelsReport || '[]'
|
order,
|
||||||
|
data.getForwardsPastDays as Forward[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getFormatString = (amount: number | string) => {
|
const getFormatString = (amount: number | string) => {
|
||||||
if (typeof amount === 'string') return amount;
|
if (typeof amount === 'string') return amount;
|
||||||
if (isType !== 'amount') {
|
if (order !== 'amount') {
|
||||||
return format({ amount });
|
return format({ amount });
|
||||||
}
|
}
|
||||||
return amount;
|
return amount;
|
||||||
|
@ -94,7 +98,7 @@ export const ForwardChannelsReport = ({ isTime, isType }: Props) => {
|
||||||
<ChannelRow key={index}>
|
<ChannelRow key={index}>
|
||||||
<TableLine>{channel.aliasIn}</TableLine>
|
<TableLine>{channel.aliasIn}</TableLine>
|
||||||
<TableLine>{channel.aliasOut}</TableLine>
|
<TableLine>{channel.aliasOut}</TableLine>
|
||||||
<LastTableLine>{getFormatString(channel[isType])}</LastTableLine>
|
<LastTableLine>{getFormatString(channel[order])}</LastTableLine>
|
||||||
</ChannelRow>
|
</ChannelRow>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -115,7 +119,7 @@ export const ForwardChannelsReport = ({ isTime, isType }: Props) => {
|
||||||
<ChannelRow key={index}>
|
<ChannelRow key={index}>
|
||||||
<TableLine>{`${channel.alias}`}</TableLine>
|
<TableLine>{`${channel.alias}`}</TableLine>
|
||||||
<DarkSubTitle>{`${channel.name}`}</DarkSubTitle>
|
<DarkSubTitle>{`${channel.name}`}</DarkSubTitle>
|
||||||
<LastTableLine>{getFormatString(channel[isType])}</LastTableLine>
|
<LastTableLine>{getFormatString(channel[order])}</LastTableLine>
|
||||||
</ChannelRow>
|
</ChannelRow>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -184,14 +188,14 @@ export const ForwardChannelsReport = ({ isTime, isType }: Props) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (parsed.length <= 0) {
|
if (forwardArray.length <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{renderTitle()}
|
{renderTitle()}
|
||||||
{renderContent(parsed)}
|
{renderContent(forwardArray)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,10 +36,10 @@ export type FlowReportType = 'tokens' | 'amount';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
days: number;
|
days: number;
|
||||||
isType: ReportType;
|
order: ReportType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ForwardReport = ({ days, isType }: Props) => {
|
export const ForwardReport = ({ days, order }: Props) => {
|
||||||
const { theme, currency, displayValues } = useConfigState();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, displayValues, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
@ -72,7 +72,7 @@ export const ForwardReport = ({ days, isType }: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLabelString = (value: number) => {
|
const getLabelString = (value: number) => {
|
||||||
if (isType === 'amount') {
|
if (order === 'amount') {
|
||||||
return numeral(value).format('0,0');
|
return numeral(value).format('0,0');
|
||||||
}
|
}
|
||||||
return format({ amount: value });
|
return format({ amount: value });
|
||||||
|
@ -84,7 +84,7 @@ export const ForwardReport = ({ days, isType }: Props) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const total = getLabelString(
|
const total = getLabelString(
|
||||||
reduced.map(x => x[isType]).reduce((a, c) => a + c, 0)
|
reduced.map(x => x[order]).reduce((a, c) => a + c, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
|
@ -101,14 +101,14 @@ export const ForwardReport = ({ days, isType }: Props) => {
|
||||||
height={110}
|
height={110}
|
||||||
padding={{
|
padding={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: isType === 'tokens' ? 80 : 50,
|
left: order === 'tokens' ? 80 : 50,
|
||||||
right: 50,
|
right: 50,
|
||||||
bottom: 10,
|
bottom: 10,
|
||||||
}}
|
}}
|
||||||
containerComponent={
|
containerComponent={
|
||||||
<VictoryVoronoiContainer
|
<VictoryVoronoiContainer
|
||||||
voronoiDimension="x"
|
voronoiDimension="x"
|
||||||
labels={({ datum }) => `${getLabelString(datum[isType])}`}
|
labels={({ datum }) => `${getLabelString(datum[order])}`}
|
||||||
labelComponent={<VictoryTooltip orientation={'bottom'} />}
|
labelComponent={<VictoryTooltip orientation={'bottom'} />}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -131,13 +131,13 @@ export const ForwardReport = ({ days, isType }: Props) => {
|
||||||
axis: { stroke: 'transparent' },
|
axis: { stroke: 'transparent' },
|
||||||
}}
|
}}
|
||||||
tickFormat={a =>
|
tickFormat={a =>
|
||||||
isType === 'tokens' ? format({ amount: a, breakNumber: true }) : a
|
order === 'tokens' ? format({ amount: a, breakNumber: true }) : a
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<VictoryBar
|
<VictoryBar
|
||||||
data={reduced}
|
data={reduced}
|
||||||
x="period"
|
x="period"
|
||||||
y={isType}
|
y={order}
|
||||||
style={{
|
style={{
|
||||||
data: {
|
data: {
|
||||||
fill: chartBarColor[theme],
|
fill: chartBarColor[theme],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { differenceInCalendarDays, differenceInHours, subDays } from 'date-fns';
|
import { differenceInCalendarDays, differenceInHours, subDays } from 'date-fns';
|
||||||
import groupBy from 'lodash.groupby';
|
import groupBy from 'lodash.groupby';
|
||||||
|
import sortBy from 'lodash.sortby';
|
||||||
import { Forward } from 'src/graphql/types';
|
import { Forward } from 'src/graphql/types';
|
||||||
|
|
||||||
type ListProps = {
|
type ListProps = {
|
||||||
|
@ -58,3 +59,94 @@ export const orderAndReducedArray = (time: number, forwardArray: Forward[]) => {
|
||||||
|
|
||||||
return reducedOrderedDay;
|
return reducedOrderedDay;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const countRoutes = (list: Forward[]) => {
|
||||||
|
const grouped = groupBy(list, 'route');
|
||||||
|
|
||||||
|
const channelInfo = [];
|
||||||
|
for (const key in grouped) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(grouped, key)) {
|
||||||
|
const element = grouped[key];
|
||||||
|
|
||||||
|
const fee = element
|
||||||
|
.map(forward => forward.fee)
|
||||||
|
.reduce((p: number, c: number) => p + c);
|
||||||
|
|
||||||
|
const tokens = element
|
||||||
|
.map(forward => forward.tokens)
|
||||||
|
.reduce((p: number, c: number) => p + c);
|
||||||
|
|
||||||
|
channelInfo.push({
|
||||||
|
aliasIn: element[0].incoming_node?.alias || 'Unknown',
|
||||||
|
aliasOut: element[0].outgoing_node?.alias || 'Unknown',
|
||||||
|
route: key,
|
||||||
|
amount: element.length,
|
||||||
|
fee,
|
||||||
|
tokens,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channelInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
const countArray = (list: Forward[], type: boolean) => {
|
||||||
|
const inOrOut = type ? 'incoming_channel' : 'outgoing_channel';
|
||||||
|
const grouped = groupBy(list, inOrOut);
|
||||||
|
|
||||||
|
const channelInfo = [];
|
||||||
|
for (const key in grouped) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(grouped, key)) {
|
||||||
|
const element = grouped[key];
|
||||||
|
|
||||||
|
const fee = element
|
||||||
|
.map(forward => forward.fee)
|
||||||
|
.reduce((p: number, c: number) => p + c);
|
||||||
|
|
||||||
|
const tokens = element
|
||||||
|
.map(forward => forward.tokens)
|
||||||
|
.reduce((p: number, c: number) => p + c);
|
||||||
|
|
||||||
|
const alias = type
|
||||||
|
? element[0].incoming_node?.alias || 'Unknown'
|
||||||
|
: element[0].outgoing_node?.alias || 'Unknown';
|
||||||
|
|
||||||
|
channelInfo.push({
|
||||||
|
alias,
|
||||||
|
name: key,
|
||||||
|
amount: element.length,
|
||||||
|
fee,
|
||||||
|
tokens,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channelInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const orderForwardChannels = (
|
||||||
|
type: string,
|
||||||
|
order: string,
|
||||||
|
forwardArray: Forward[]
|
||||||
|
) => {
|
||||||
|
if (type === 'route') {
|
||||||
|
const mapped = forwardArray.map(forward => {
|
||||||
|
return {
|
||||||
|
route: `${forward.incoming_channel} - ${forward.outgoing_channel}`,
|
||||||
|
...forward,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const grouped = countRoutes(mapped);
|
||||||
|
|
||||||
|
const sortedRoute = sortBy(grouped, order).reverse().slice(0, 10);
|
||||||
|
return sortedRoute;
|
||||||
|
}
|
||||||
|
if (type === 'incoming') {
|
||||||
|
const incomingCount = countArray(forwardArray, true);
|
||||||
|
const sortedInCount = sortBy(incomingCount, order).reverse().slice(0, 10);
|
||||||
|
return sortedInCount;
|
||||||
|
}
|
||||||
|
const outgoingCount = countArray(forwardArray, false);
|
||||||
|
const sortedOutCount = sortBy(outgoingCount, order).reverse().slice(0, 10);
|
||||||
|
return sortedOutCount;
|
||||||
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
Separation,
|
Separation,
|
||||||
} from '../../../../components/generic/Styled';
|
} from '../../../../components/generic/Styled';
|
||||||
import { mediaWidths } from '../../../../styles/Themes';
|
import { mediaWidths } from '../../../../styles/Themes';
|
||||||
import { ForwardReport, ReportDuration, ReportType } from './ForwardReport';
|
import { ForwardReport, ReportType } from './ForwardReport';
|
||||||
import { ForwardChannelsReport } from './ForwardChannelReport';
|
import { ForwardChannelsReport } from './ForwardChannelReport';
|
||||||
import { ButtonRow } from './Buttons';
|
import { ButtonRow } from './Buttons';
|
||||||
|
|
||||||
|
@ -24,9 +24,8 @@ export const CardContent = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ForwardBox = () => {
|
export const ForwardBox = () => {
|
||||||
const [isTime, setIsTime] = useState<ReportDuration>('month');
|
|
||||||
const [days, setDays] = useState<number>(30);
|
const [days, setDays] = useState<number>(30);
|
||||||
const [isType, setIsType] = useState<ReportType>('amount');
|
const [order, setOrder] = useState<ReportType>('amount');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardWithTitle>
|
<CardWithTitle>
|
||||||
|
@ -35,15 +34,14 @@ export const ForwardBox = () => {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Card mobileCardPadding={'8px'}>
|
<Card mobileCardPadding={'8px'}>
|
||||||
<ButtonRow
|
<ButtonRow
|
||||||
isTime={isTime}
|
days={days}
|
||||||
isType={isType}
|
order={order}
|
||||||
setDays={setDays}
|
setDays={setDays}
|
||||||
setIsTime={setIsTime}
|
setOrder={setOrder}
|
||||||
setIsType={setIsType}
|
|
||||||
/>
|
/>
|
||||||
<ForwardReport days={days} isType={isType} />
|
<ForwardReport days={days} order={order} />
|
||||||
<Separation />
|
<Separation />
|
||||||
<ForwardChannelsReport isTime={isTime} isType={isType} />
|
<ForwardChannelsReport days={days} order={order} />
|
||||||
</Card>
|
</Card>
|
||||||
</CardWithTitle>
|
</CardWithTitle>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Reference in a new issue