Skip to content

Commit

Permalink
Merge pull request #241 from apotdevin/transaction-filters
Browse files Browse the repository at this point in the history
chore: transaction filters
  • Loading branch information
apotdevin authored Apr 24, 2021
2 parents 3322318 + 7e721e8 commit 128bcaa
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 39 deletions.
18 changes: 7 additions & 11 deletions config/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,18 @@ function createApolloClient(context?: ResolverContext) {
fields: {
getResume: {
keyArgs: [],
merge(existing, incoming, { args }) {
if (!existing) {
return incoming;
}
merge(existing, incoming) {
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];
}
const current = existing?.resume ? [...existing.resume] : [];
const newIncoming = incoming?.resume
? [...incoming.resume]
: [];

return {
...existing,
offset: incoming.offset,
resume: merged,
resume: [...current, ...newIncoming],
};
},
},
Expand Down
114 changes: 94 additions & 20 deletions pages/transactions.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify';
import { InvoiceCard } from 'src/views/transactions/InvoiceCard';
import { useGetResumeQuery } from 'src/graphql/queries/__generated__/getResume.generated';
import {
GetResumeQuery,
useGetResumeQuery,
} from 'src/graphql/queries/__generated__/getResume.generated';
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';

import { NextPageContext } from 'next';
import { getProps } from 'src/utils/ssr';
import { RefreshCw } from 'react-feather';
import { RefreshCw, Settings } from 'react-feather';
import styled, { css } from 'styled-components';
import {
Card,
CardWithTitle,
SubTitle,
SingleLine,
DarkSubTitle,
} from '../src/components/generic/Styled';
import { getErrorContent } from '../src/utils/error';
import { PaymentsCard } from '../src/views/transactions/PaymentsCards';
import { LoadingCard } from '../src/components/loading/LoadingCard';
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
import { FlowBox } from '../src/views/home/reports/flow';
import { useLocalStorage } from 'src/hooks/UseLocalStorage';
import { useNodeInfo } from 'src/hooks/UseNodeInfo';
import {
defaultSettings,
TransactionSettings,
} from 'src/views/transactions/Settings';
import { subDays, format } from 'date-fns';

type RotationProps = {
withRotation: boolean;
};

type ResumeTransactions = GetResumeQuery['getResume']['resume'];

const Rotation = styled.div<RotationProps>`
${({ withRotation }) =>
withRotation &&
Expand All @@ -45,8 +58,14 @@ const TransactionsView = () => {
const [isPolling, setIsPolling] = useState(false);
const [indexOpen, setIndexOpen] = useState(0);

const [open, setOpen] = useState<boolean>(false);

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

const { publicKey } = useNodeInfo();

const [settings] = useLocalStorage('transactionSettings', defaultSettings);

const {
data,
fetchMore,
Expand Down Expand Up @@ -79,7 +98,42 @@ const TransactionsView = () => {
return <LoadingCard title={'Transactions'} />;
}

const resumeList = data.getResume.resume;
const beforeDate = subDays(new Date(), offset);

const selfInvoices: string[] = data.getResume.resume.reduce((p, c) => {
if (!c) return p;
if (c.__typename === 'PaymentType') {
if (c.destination === publicKey) {
return [...p, c.id];
}
}
return p;
}, [] as string[]);

const resumeList = data.getResume.resume?.reduce((p, c) => {
const { rebalance, confirmed } = settings;

if (!c) return p;

if (rebalance) {
if (c.__typename === 'PaymentType') {
if (c.destination === publicKey) {
return p;
}
}
if (selfInvoices.includes(c.id)) {
return p;
}
}

if (confirmed) {
if (!c.is_confirmed) {
return p;
}
}

return [...p, c];
}, [] as ResumeTransactions);

const handleClick = (limit: number) =>
fetchMore({ variables: { offset, limit } });
Expand All @@ -89,24 +143,44 @@ const TransactionsView = () => {
<FlowBox />
<CardWithTitle>
<SingleLine>
<SubTitle>Transactions</SubTitle>
<ColorButton
withMargin={'0 0 8px'}
onClick={() => {
if (isPolling) {
setIsPolling(false);
stopPolling();
} else {
setIsPolling(true);
startPolling(1000);
}
}}
>
<Rotation withRotation={isPolling}>
<RefreshCw size={18} />
</Rotation>
</ColorButton>
<SubTitle>
Transactions
<DarkSubTitle fontSize={'12px'}>
{`${format(beforeDate, 'dd/MM/yy')} - Today`}
</DarkSubTitle>
</SubTitle>
<SingleLine>
<ColorButton
withMargin={'0 0 8px 8px'}
onClick={() => {
setOpen(p => !p);
}}
>
<Settings size={18} />
</ColorButton>
<ColorButton
withMargin={'0 0 8px'}
onClick={() => {
if (isPolling) {
setIsPolling(false);
stopPolling();
} else {
setIsPolling(true);
startPolling(1000);
}
}}
>
<Rotation withRotation={isPolling}>
<RefreshCw size={18} />
</Rotation>
</ColorButton>
</SingleLine>
</SingleLine>
{open && (
<Card>
<TransactionSettings />
</Card>
)}
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
{resumeList?.map((entry, index: number) => {
if (!entry) {
Expand Down
2 changes: 1 addition & 1 deletion server/schema/transactions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ export const transactionTypes = gql`
type getResumeType {
offset: Int
resume: [Transaction]
resume: [Transaction]!
}
`;
2 changes: 1 addition & 1 deletion server/schema/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const queryTypes = gql`
getNode(publicKey: String!, withoutChannels: Boolean): Node!
decodeRequest(request: String!): decodeType
getWalletInfo: walletInfoType
getResume(offset: Int, limit: Int): getResumeType
getResume(offset: Int, limit: Int): getResumeType!
getForwards(days: Int!): [Forward]!
getBitcoinPrice(logger: Boolean, currency: String): String
getBitcoinFees(logger: Boolean): bitcoinFeeType
Expand Down
3 changes: 3 additions & 0 deletions src/components/input/InputWithDeco.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type InputWithDecoProps = {
placeholder?: string;
inputType?: string;
inputCallback?: (value: string) => void;
blurCallback?: (value: string) => void;
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
onEnter?: () => void;
};
Expand All @@ -64,6 +65,7 @@ export const InputWithDeco: React.FC<InputWithDecoProps> = ({
inputMaxWidth,
inputType = 'text',
inputCallback,
blurCallback,
onKeyDown,
onEnter,
}) => {
Expand Down Expand Up @@ -100,6 +102,7 @@ export const InputWithDeco: React.FC<InputWithDecoProps> = ({
mobileMargin={'0'}
type={inputType}
onChange={e => inputCallback && inputCallback(e.target.value)}
onBlur={e => blurCallback && blurCallback(e.target.value)}
{...onKeyDownProp}
{...props}
/>
Expand Down
3 changes: 3 additions & 0 deletions src/components/input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ interface InputCompProps {
mobileFullWidth?: boolean;
maxWidth?: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onBlur?: (e: React.ChangeEvent<HTMLInputElement>) => void;
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
onEnter?: () => void;
}
Expand All @@ -99,6 +100,7 @@ export const Input = ({
fullWidth = true,
maxWidth,
onChange,
onBlur,
onKeyDown,
onEnter,
}: InputCompProps) => {
Expand All @@ -112,6 +114,7 @@ export const Input = ({
withMargin={withMargin}
mobileMargin={mobileMargin}
onChange={onChange}
onBlur={onBlur}
fullWidth={fullWidth}
mobileFullWidth={mobileFullWidth}
maxWidth={maxWidth}
Expand Down
8 changes: 4 additions & 4 deletions src/graphql/queries/__generated__/getResume.generated.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export type Query = {
getNode: Node;
decodeRequest?: Maybe<DecodeType>;
getWalletInfo?: Maybe<WalletInfoType>;
getResume?: Maybe<GetResumeType>;
getResume: GetResumeType;
getForwards: Array<Maybe<Forward>>;
getBitcoinPrice?: Maybe<Scalars['String']>;
getBitcoinFees?: Maybe<BitcoinFeeType>;
Expand Down Expand Up @@ -914,7 +914,7 @@ export type Transaction = InvoiceType | PaymentType;
export type GetResumeType = {
__typename?: 'getResumeType';
offset?: Maybe<Scalars['Int']>;
resume?: Maybe<Array<Maybe<Transaction>>>;
resume: Array<Maybe<Transaction>>;
};

export type ChannelHealth = {
Expand Down
67 changes: 67 additions & 0 deletions src/hooks/UseLocalStorage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useEffect, useState } from 'react';

export const useLocalStorage = <T,>(
key: string,
initialValue: T
): [T, (value: T) => void] => {
const readValue = () => {
if (typeof window === 'undefined') {
return initialValue;
}

try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.warn(`Error reading localStorage key “${key}”:`, error);
return initialValue;
}
};

const [storedValue, setStoredValue] = useState<T>(readValue);

const setValue = (value: T) => {
if (typeof window == 'undefined') {
console.warn(
`Tried setting localStorage key “${key}” even though environment is not a client`
);
}

try {
const newValue = value instanceof Function ? value(storedValue) : value;
window.localStorage.setItem(key, JSON.stringify(newValue));

setStoredValue(newValue);

// We dispatch a custom event so every useLocalStorage hook are notified
window.dispatchEvent(new Event('local-storage'));
} catch (error) {
console.warn(`Error setting localStorage key “${key}”:`, error);
}
};

useEffect(() => {
setStoredValue(readValue());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const handleStorageChange = () => {
setStoredValue(readValue());
};

// this only works for other documents, not the current one
window.addEventListener('storage', handleStorageChange);

// this is a custom event, triggered in writeValueToLocalStorage
window.addEventListener('local-storage', handleStorageChange);

return () => {
window.removeEventListener('storage', handleStorageChange);
window.removeEventListener('local-storage', handleStorageChange);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return [storedValue, setValue];
};
Loading

0 comments on commit 128bcaa

Please sign in to comment.