mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-20 13:34:30 +01:00
chore: 🔧 add score query
This commit is contained in:
parent
d1fec2b92d
commit
7fa7cfc3c2
55 changed files with 1863 additions and 52 deletions
|
@ -43,9 +43,10 @@ module.exports = {
|
|||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
'import/no-unresolved': 'off',
|
||||
'import/order': 2,
|
||||
'no-unused-vars': 2,
|
||||
'no-unused-vars': 0,
|
||||
camelcase: 'off',
|
||||
'@typescript-eslint/camelcase': 'off',
|
||||
'react/prop-types': 'off',
|
||||
|
|
110
package-lock.json
generated
110
package-lock.json
generated
|
@ -8533,6 +8533,12 @@
|
|||
"integrity": "sha512-6+OPzqhKX/cx5xh+yO8Cqg3u3alrkhoxhE5ZOdSEv0DOzJ13lwJ6laqGU0Kv6+XDMFmlnGId04LtY22PsFLQUw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/d3-array": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.8.0.tgz",
|
||||
"integrity": "sha512-Q0ubcGHAmCRPh90/hoYB4eKWhxYKUxphwSeQrlz2tiabQ8S9zqhaE2CZJtCaLH2cjqKcjr52WPvmOA7ha0O4ZA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/d3-chord": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.10.tgz",
|
||||
|
@ -8557,9 +8563,9 @@
|
|||
"integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ=="
|
||||
},
|
||||
"@types/d3-scale": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.1.tgz",
|
||||
"integrity": "sha512-j+FryQSVk3GHLqjOX/RsHwGHg4XByJ0xIO1ASBTgzhE9o1tgeV4kEWLOzMzJRembKalflk5F03lEkM+4V6LDrQ==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.2.tgz",
|
||||
"integrity": "sha512-qpQe8G02tzUwt9sdWX1h8A/W0Q1+N48wMnYXVOkrzeLUkCfvzJYV9Ee3aORCS4dN4ONRLFmMvaXdziQ29XGLjQ==",
|
||||
"requires": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
|
@ -8915,7 +8921,6 @@
|
|||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz",
|
||||
"integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
|
@ -9437,6 +9442,16 @@
|
|||
"resolved": "https://registry.npmjs.org/@ungap/global-this/-/global-this-0.4.2.tgz",
|
||||
"integrity": "sha512-uFg7Kz+E12RBlgBLMlWVjmn2OIeE2J1Lzij0RseNcCVsrJX+LEB4fQ9MnoPXkXJmO5cHtTEzI5ATtb3IJfQ9tQ=="
|
||||
},
|
||||
"@visx/bounds": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-1.0.0.tgz",
|
||||
"integrity": "sha512-QxD/OkZVkzpeP6L0YxUnIAsxlFemkDPfOumchVDRlrO4lZ3YXLmsnaEEiJpU5tSgNamZAUh+Tz3d2RbHp3qqxA==",
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"@visx/chord": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/chord/-/chord-1.0.0.tgz",
|
||||
|
@ -9459,6 +9474,15 @@
|
|||
"d3-shape": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"@visx/event": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/event/-/event-1.0.0.tgz",
|
||||
"integrity": "sha512-GQFsLVVbVs1elvRMNP+dscB/hOvYpt/zRxnJgoBIYgAJ5HJZ4n2PweSKagdaDB/MSCAvRZUj/iM6PMTZXnlnew==",
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"@visx/point": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@visx/group": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/group/-/group-1.0.0.tgz",
|
||||
|
@ -9470,6 +9494,11 @@
|
|||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"@visx/point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/point/-/point-1.0.0.tgz",
|
||||
"integrity": "sha512-0L3ILwv6ro0DsQVbA1lo8fo6q3wvIeSTt9C8NarUUkoTNSFZaJtlmvwg2238r8fwwmSv0v9QFBj1hBz4o0bHrg=="
|
||||
},
|
||||
"@visx/responsive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-1.1.0.tgz",
|
||||
|
@ -9496,9 +9525,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"d3-array": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.8.0.tgz",
|
||||
"integrity": "sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw=="
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz",
|
||||
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg=="
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "3.2.3",
|
||||
|
@ -9534,6 +9563,19 @@
|
|||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"@visx/tooltip": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-1.1.0.tgz",
|
||||
"integrity": "sha512-xVB1vjZoL6OqiLp2VjyUYAoklPm/pQJmOD90TaBnLjku9KyYmElziO4q2qEpWmtY+K4XjXful10gBRPkt/e70Q==",
|
||||
"requires": {
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/react": "*",
|
||||
"@visx/bounds": "1.0.0",
|
||||
"classnames": "^2.2.5",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-use-measure": "2.0.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
|
||||
|
@ -14467,9 +14509,9 @@
|
|||
}
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz",
|
||||
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg=="
|
||||
},
|
||||
"d3-chord": {
|
||||
"version": "1.0.6",
|
||||
|
@ -14478,6 +14520,13 @@
|
|||
"requires": {
|
||||
"d3-array": "1",
|
||||
"d3-path": "1"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"d3-collection": {
|
||||
|
@ -14525,6 +14574,21 @@
|
|||
"d3-interpolate": "1",
|
||||
"d3-time": "1",
|
||||
"d3-time-format": "2"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
|
||||
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d3-shape": {
|
||||
|
@ -14541,11 +14605,11 @@
|
|||
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
|
||||
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
|
||||
"integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
"d3-time": "1 - 2"
|
||||
}
|
||||
},
|
||||
"d3-timer": {
|
||||
|
@ -14652,8 +14716,7 @@
|
|||
"debounce": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
|
||||
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
|
@ -25547,6 +25610,14 @@
|
|||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"react-use-measure": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.0.1.tgz",
|
||||
"integrity": "sha512-lFfHiqcXbJ2/6aUkZwt8g5YYM7EGqNVxJhMqMPqv1BVXRKp8D7jYLlmma0SvhRY4WYxxkZpCdbJvhDylb5gcEA==",
|
||||
"requires": {
|
||||
"debounce": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
|
||||
|
@ -29060,6 +29131,13 @@
|
|||
"lodash": "^4.17.19",
|
||||
"prop-types": "^15.5.8",
|
||||
"victory-core": "^35.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"victory-brush-container": {
|
||||
|
|
|
@ -36,10 +36,13 @@
|
|||
"@apollo/client": "^3.3.5",
|
||||
"@next/bundle-analyzer": "^10.0.3",
|
||||
"@visx/chord": "^1.0.0",
|
||||
"@visx/curve": "^1.0.0",
|
||||
"@visx/event": "^1.0.0",
|
||||
"@visx/group": "^1.0.0",
|
||||
"@visx/responsive": "^1.1.0",
|
||||
"@visx/scale": "^1.1.0",
|
||||
"@visx/shape": "^1.2.0",
|
||||
"@visx/tooltip": "^1.1.0",
|
||||
"apollo-server-micro": "^2.19.0",
|
||||
"balanceofsatoshis": "^7.10.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
@ -50,6 +53,7 @@
|
|||
"boltz-core": "^0.3.5",
|
||||
"cookie": "^0.4.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"d3-array": "^2.9.1",
|
||||
"date-fns": "^2.16.1",
|
||||
"graphql": "^15.4.0",
|
||||
"graphql-iso-date": "^3.6.1",
|
||||
|
@ -109,6 +113,7 @@
|
|||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cookie": "^0.4.0",
|
||||
"@types/crypto-js": "^4.0.1",
|
||||
"@types/d3-array": "^2.8.0",
|
||||
"@types/graphql-iso-date": "^3.4.0",
|
||||
"@types/js-cookie": "^2.2.6",
|
||||
"@types/js-yaml": "^3.12.5",
|
||||
|
|
|
@ -7,6 +7,7 @@ import { StyledToastContainer } from 'src/components/toastContainer/ToastContain
|
|||
import { AppProps } from 'next/app';
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { useApollo } from 'config/client';
|
||||
import { BaseProvider } from 'src/context/BaseContext';
|
||||
import { ContextProvider } from '../src/context/ContextProvider';
|
||||
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||
|
@ -46,11 +47,13 @@ export default function App({ Component, pageProps }: AppProps) {
|
|||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</Head>
|
||||
<ConfigProvider initialConfig={pageProps.initialConfig}>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
<BaseProvider initialHasToken={pageProps.hasToken}>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</BaseProvider>
|
||||
</ConfigProvider>
|
||||
<StyledToastContainer />
|
||||
</ApolloProvider>
|
||||
|
|
61
pages/scores/[id].tsx
Normal file
61
pages/scores/[id].tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { NodeInfo } from 'src/views/scores/NodeInfo';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useBaseDispatch, useBaseState } from 'src/context/BaseContext';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import { NodeScores } from 'src/views/scores/NodeScores';
|
||||
import { Graph } from 'src/views/scores/NodeGraph';
|
||||
import { useDeleteBaseTokenMutation } from 'src/graphql/mutations/__generated__/deleteBaseToken.generated';
|
||||
|
||||
const NodeScoreView = () => {
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
const { hasToken } = useBaseState();
|
||||
const dispatch = useBaseDispatch();
|
||||
|
||||
const [deleteToken] = useDeleteBaseTokenMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasToken) {
|
||||
push(appendBasePath('/token'));
|
||||
}
|
||||
}, [hasToken, push]);
|
||||
|
||||
const handleAuthError = () => {
|
||||
dispatch({ type: 'change', hasToken: false });
|
||||
deleteToken();
|
||||
push(appendBasePath('/token'));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!loading && (
|
||||
<>
|
||||
<Graph />
|
||||
<NodeInfo />
|
||||
</>
|
||||
)}
|
||||
<NodeScores
|
||||
callback={() => setLoading(false)}
|
||||
errorCallback={handleAuthError}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<NodeScoreView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
104
pages/scores/index.tsx
Normal file
104
pages/scores/index.tsx
Normal file
|
@ -0,0 +1,104 @@
|
|||
import React from 'react';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { useGetBosScoresQuery } from 'src/graphql/queries/__generated__/getBosScores.generated';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import { ScoreCard } from 'src/views/scores/ScoreCard';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
DarkSubTitle,
|
||||
Separation,
|
||||
SingleLine,
|
||||
SubTitle,
|
||||
} from 'src/components/generic/Styled';
|
||||
import { useNodeInfo } from 'src/hooks/UseNodeInfo';
|
||||
import { getFormatDate, getNodeLink } from 'src/components/generic/helpers';
|
||||
import { Table } from 'src/components/table';
|
||||
import { BarChart2 } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import { chartColors } from 'src/styles/Themes';
|
||||
import { Link } from 'src/components/link/Link';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
|
||||
const S = {
|
||||
Icon: styled.div`
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: ${chartColors.orange};
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
const Wrapped = () => {
|
||||
const { publicKey } = useNodeInfo();
|
||||
const { data, loading } = useGetBosScoresQuery({
|
||||
onError: err => toast.error(getErrorContent(err)),
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<GridWrapper>
|
||||
<LoadingCard title={'BOS Scores'} />
|
||||
</GridWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const scores = data?.getBosScores?.scores.filter(Boolean) || [];
|
||||
const date = data?.getBosScores?.updated || '';
|
||||
|
||||
const thisNode =
|
||||
scores.find(score => score?.public_key === publicKey) || null;
|
||||
|
||||
const columns = [
|
||||
{ Header: 'Index', accessor: 'index' },
|
||||
{ Header: 'Alias', accessor: 'alias' },
|
||||
{ Header: 'PubKey', accessor: 'key' },
|
||||
{ Header: 'Position', accessor: 'position' },
|
||||
{ Header: 'Score', accessor: 'score' },
|
||||
{ Header: 'History', accessor: 'chart' },
|
||||
];
|
||||
|
||||
const tableData = scores.map((s, index) => ({
|
||||
...s,
|
||||
index: index + 1,
|
||||
key: getNodeLink(s?.public_key),
|
||||
chart: (
|
||||
<Link to={`/scores/${s?.public_key}`}>
|
||||
<S.Icon>
|
||||
<BarChart2 />
|
||||
</S.Icon>
|
||||
</Link>
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<GridWrapper>
|
||||
<CardWithTitle>
|
||||
<SingleLine>
|
||||
<SubTitle>BOS Scores</SubTitle>
|
||||
<DarkSubTitle>{`Updated: ${getFormatDate(date)}`}</DarkSubTitle>
|
||||
</SingleLine>
|
||||
<Card>
|
||||
<ScoreCard score={thisNode} />
|
||||
<Separation />
|
||||
<Table
|
||||
filterPlaceholder={'nodes'}
|
||||
withBorder={true}
|
||||
tableData={tableData}
|
||||
tableColumns={columns}
|
||||
/>
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
</GridWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
41
pages/token.tsx
Normal file
41
pages/token.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import React, { useState } from 'react';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { TokenCard } from 'src/views/token/TokenCard';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { PaidCard } from 'src/views/token/PaidCard';
|
||||
import { useBaseState } from 'src/context/BaseContext';
|
||||
import { Card } from 'src/components/generic/Styled';
|
||||
import { RecoverToken } from 'src/views/token/RecoverToken';
|
||||
|
||||
const TokenView = () => {
|
||||
const { hasToken } = useBaseState();
|
||||
const [id, setId] = useState<string | null>();
|
||||
|
||||
if (id) {
|
||||
return <PaidCard id={id} />;
|
||||
}
|
||||
|
||||
if (hasToken) {
|
||||
return <Card>You already have a token!</Card>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TokenCard paidCallback={id => setId(id)} />
|
||||
<RecoverToken />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<TokenView />
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
|
@ -22,7 +22,11 @@ export const bitcoinResolvers = {
|
|||
throw new Error('Problem getting Bitcoin price.');
|
||||
}
|
||||
},
|
||||
getBitcoinFees: async (_: undefined, params: any, context: ContextType) => {
|
||||
getBitcoinFees: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'bitcoinFee');
|
||||
|
||||
try {
|
||||
|
|
|
@ -32,7 +32,7 @@ export const chainResolvers = {
|
|||
Query: {
|
||||
getChainBalance: async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'chainBalance');
|
||||
|
@ -48,16 +48,14 @@ export const chainResolvers = {
|
|||
},
|
||||
getPendingChainBalance: async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'pendingChainBalance');
|
||||
|
||||
const { lnd } = context;
|
||||
|
||||
const pendingValue: PendingChainBalanceProps = await to<
|
||||
GetPendingChainBalanceType
|
||||
>(
|
||||
const pendingValue: PendingChainBalanceProps = await to<GetPendingChainBalanceType>(
|
||||
getPendingChainBalance({
|
||||
lnd,
|
||||
})
|
||||
|
@ -66,7 +64,7 @@ export const chainResolvers = {
|
|||
},
|
||||
getChainTransactions: async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'chainTransactions');
|
||||
|
@ -85,7 +83,7 @@ export const chainResolvers = {
|
|||
).reverse();
|
||||
return transactions;
|
||||
},
|
||||
getUtxos: async (_: undefined, params: any, context: ContextType) => {
|
||||
getUtxos: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getUtxos');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -11,7 +11,7 @@ interface ChannelBalanceProps {
|
|||
|
||||
export const getChannelBalance = async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'channelBalance');
|
||||
|
|
|
@ -26,7 +26,7 @@ interface PendingChannelProps {
|
|||
|
||||
export const getPendingChannels = async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'pendingChannels');
|
||||
|
|
|
@ -47,6 +47,7 @@ export const getContext = (context: ResolverContext) => {
|
|||
const cookies = cookie.parse(req.headers.cookie ?? '') || {};
|
||||
const auth = cookies[appConstants.cookieName];
|
||||
const lnMarketsAuth = cookies[appConstants.lnMarketsAuth];
|
||||
const tokenAuth = cookies[appConstants.tokenCookieName];
|
||||
|
||||
let lnd: LndObject | null = null;
|
||||
let id: string | null = null;
|
||||
|
@ -72,6 +73,7 @@ export const getContext = (context: ResolverContext) => {
|
|||
accounts: accountConfig,
|
||||
res,
|
||||
lnMarketsAuth,
|
||||
tokenAuth,
|
||||
};
|
||||
|
||||
return resolverContext;
|
||||
|
|
|
@ -8,7 +8,7 @@ export const githubResolvers = {
|
|||
Query: {
|
||||
getLatestVersion: async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'getLnPay');
|
||||
|
|
|
@ -15,7 +15,7 @@ type ChannelFeesType = {
|
|||
myFeeRate: number;
|
||||
};
|
||||
|
||||
export default async (_: undefined, params: any, context: ContextType) => {
|
||||
export default async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getFeeHealth');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { getChannelVolume, getChannelIdInfo, getAverage } from '../helpers';
|
|||
|
||||
const monthInBlocks = 4380;
|
||||
|
||||
export default async (_: undefined, params: any, context: ContextType) => {
|
||||
export default async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getVolumeHealth');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -16,7 +16,11 @@ interface NetworkInfoProps {
|
|||
|
||||
export const networkResolvers = {
|
||||
Query: {
|
||||
getNetworkInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||
getNetworkInfo: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'networkInfo');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -28,7 +28,7 @@ export const nodeResolvers = {
|
|||
|
||||
return { lnd, publicKey, withChannels: !withoutChannels };
|
||||
},
|
||||
getNodeInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||
getNodeInfo: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'nodeInfo');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -19,7 +19,7 @@ interface PeerProps {
|
|||
|
||||
export const peerResolvers = {
|
||||
Query: {
|
||||
getPeers: async (_: undefined, params: any, context: ContextType) => {
|
||||
getPeers: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getPeers');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -3,6 +3,10 @@ import { requestLimiter } from 'server/helpers/rateLimiter';
|
|||
import { toWithError } from 'server/helpers/async';
|
||||
import { appUrls } from 'server/utils/appUrls';
|
||||
import { request, gql } from 'graphql-request';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { appConstants } from 'server/utils/appConstants';
|
||||
import cookieLib from 'cookie';
|
||||
|
||||
const getBaseCanConnectQuery = gql`
|
||||
{
|
||||
|
@ -10,6 +14,16 @@ const getBaseCanConnectQuery = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
const getBaseInfoQuery = gql`
|
||||
{
|
||||
getInfo {
|
||||
lastBosUpdate
|
||||
apiTokenSatPrice
|
||||
apiTokenOriginalSatPrice
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const getBaseNodesQuery = gql`
|
||||
{
|
||||
getNodes {
|
||||
|
@ -39,6 +53,15 @@ const createBaseInvoiceQuery = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
const createBaseTokenInvoiceQuery = gql`
|
||||
mutation CreateTokenInvoice($days: Int) {
|
||||
createTokenInvoice(days: $days) {
|
||||
request
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const createThunderPointsQuery = gql`
|
||||
mutation CreatePoints(
|
||||
$id: String!
|
||||
|
@ -50,8 +73,55 @@ const createThunderPointsQuery = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
const createBaseTokenQuery = gql`
|
||||
mutation CreateBaseToken($id: String!) {
|
||||
createBaseToken(id: $id)
|
||||
}
|
||||
`;
|
||||
|
||||
const getBosScoresQuery = gql`
|
||||
{
|
||||
getBosScores {
|
||||
updated
|
||||
scores {
|
||||
alias
|
||||
public_key
|
||||
score
|
||||
updated
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const getBosNodeScoresQuery = gql`
|
||||
query GetNodeScores($publicKey: String!, $token: String!) {
|
||||
getNodeScores(publicKey: $publicKey, token: $token) {
|
||||
alias
|
||||
public_key
|
||||
score
|
||||
updated
|
||||
position
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const tbaseResolvers = {
|
||||
Query: {
|
||||
getBaseInfo: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getBaseInfo');
|
||||
|
||||
const [data, error] = await toWithError(
|
||||
request(appUrls.tbase, getBaseInfoQuery)
|
||||
);
|
||||
|
||||
if (error || !data?.getInfo) {
|
||||
logger.error('Error getting info: %o', { error });
|
||||
throw new GraphQLError('ErrorGettingInfo');
|
||||
}
|
||||
|
||||
return data.getInfo;
|
||||
},
|
||||
getBaseCanConnect: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
|
@ -67,6 +137,46 @@ export const tbaseResolvers = {
|
|||
|
||||
return true;
|
||||
},
|
||||
getBosNodeScores: async (
|
||||
_: undefined,
|
||||
{ publicKey }: { publicKey: string },
|
||||
{ ip, tokenAuth }: ContextType
|
||||
) => {
|
||||
if (!tokenAuth) {
|
||||
logger.error('No ThunderBase auth token available');
|
||||
throw new GraphQLError('NotAuthenticated');
|
||||
}
|
||||
|
||||
await requestLimiter(ip, 'getBosNodeScores');
|
||||
|
||||
const [data, error] = await toWithError(
|
||||
request(appUrls.tbase, getBosNodeScoresQuery, {
|
||||
publicKey,
|
||||
token: tokenAuth,
|
||||
})
|
||||
);
|
||||
|
||||
if (error) {
|
||||
logger.error('Error getting BOS scores: %o', { error });
|
||||
throw new GraphQLError('ErrorGettingBosScores');
|
||||
}
|
||||
|
||||
return data?.getNodeScores || [];
|
||||
},
|
||||
getBosScores: async (_: undefined, __: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getBosScores');
|
||||
|
||||
const [data, error] = await toWithError(
|
||||
request(appUrls.tbase, getBosScoresQuery)
|
||||
);
|
||||
|
||||
if (error || !data?.getBosScores) {
|
||||
logger.error('Error getting BOS scores: %o', { error });
|
||||
throw new GraphQLError('ErrorGettingBosScores');
|
||||
}
|
||||
|
||||
return data.getBosScores;
|
||||
},
|
||||
getBaseNodes: async (_: undefined, __: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getBaseNodes');
|
||||
|
||||
|
@ -98,7 +208,7 @@ export const tbaseResolvers = {
|
|||
params: { amount: number },
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'getBaseInvoice');
|
||||
await requestLimiter(context.ip, 'createBaseInvoice');
|
||||
|
||||
if (!params?.amount) return '';
|
||||
|
||||
|
@ -111,12 +221,79 @@ export const tbaseResolvers = {
|
|||
|
||||
return null;
|
||||
},
|
||||
createBaseToken: async (
|
||||
_: undefined,
|
||||
{ id }: { id: string },
|
||||
{ ip, res }: ContextType
|
||||
) => {
|
||||
await requestLimiter(ip, 'createBaseInvoice');
|
||||
|
||||
const [data, error] = await toWithError(
|
||||
request(appUrls.tbase, createBaseTokenQuery, { id })
|
||||
);
|
||||
|
||||
if (error || !data?.createBaseToken) {
|
||||
logger.debug('Error getting thunderbase token: %o', { error });
|
||||
throw new Error('ErrorGettingToken');
|
||||
}
|
||||
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookieLib.serialize(
|
||||
appConstants.tokenCookieName,
|
||||
data.createBaseToken,
|
||||
{
|
||||
maxAge: 60 * 60 * 24 * 30, //One month
|
||||
httpOnly: true,
|
||||
sameSite: true,
|
||||
path: '/',
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
},
|
||||
deleteBaseToken: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
{ ip, res }: ContextType
|
||||
) => {
|
||||
await requestLimiter(ip, 'deleteBaseToken');
|
||||
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookieLib.serialize(appConstants.tokenCookieName, '', {
|
||||
maxAge: -1,
|
||||
httpOnly: true,
|
||||
sameSite: true,
|
||||
path: '/',
|
||||
})
|
||||
);
|
||||
return true;
|
||||
},
|
||||
createBaseTokenInvoice: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'createBaseTokenInvoice');
|
||||
|
||||
const [data, error] = await toWithError(
|
||||
request(appUrls.tbase, createBaseTokenInvoiceQuery)
|
||||
);
|
||||
|
||||
if (error || !data?.createTokenInvoice) {
|
||||
logger.error('Error getting invoice for token: %o', error);
|
||||
throw new Error('ErrorGettingInvoice');
|
||||
}
|
||||
return data.createTokenInvoice;
|
||||
},
|
||||
createThunderPoints: async (
|
||||
_: undefined,
|
||||
params: { id: string; alias: string; uris: string[]; public_key: string },
|
||||
context: ContextType
|
||||
): Promise<boolean> => {
|
||||
await requestLimiter(context.ip, 'getThunderPoints');
|
||||
await requestLimiter(context.ip, 'createThunderPoints');
|
||||
|
||||
const [info, error] = await toWithError(
|
||||
request(appUrls.tbase, createThunderPointsQuery, params)
|
||||
|
|
|
@ -17,4 +17,23 @@ export const tbaseTypes = gql`
|
|||
id: String!
|
||||
request: String!
|
||||
}
|
||||
|
||||
type BosScore {
|
||||
alias: String!
|
||||
public_key: String!
|
||||
score: Int!
|
||||
updated: String!
|
||||
position: Int!
|
||||
}
|
||||
|
||||
type BosScoreResponse {
|
||||
updated: String!
|
||||
scores: [BosScore!]!
|
||||
}
|
||||
|
||||
type BaseInfo {
|
||||
lastBosUpdate: String!
|
||||
apiTokenSatPrice: Int!
|
||||
apiTokenOriginalSatPrice: Int!
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -68,7 +68,7 @@ export const toolsResolvers = {
|
|||
throw new Error(getErrorMsg(error));
|
||||
}
|
||||
},
|
||||
getBackups: async (_: undefined, params: any, context: ContextType) => {
|
||||
getBackups: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getBackups');
|
||||
|
||||
const { lnd } = context;
|
||||
|
@ -83,7 +83,7 @@ export const toolsResolvers = {
|
|||
throw new Error(getErrorMsg(error));
|
||||
}
|
||||
},
|
||||
adminCheck: async (_: undefined, params: any, context: ContextType) => {
|
||||
adminCheck: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'adminCheck');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -28,6 +28,9 @@ export const generalTypes = gql`
|
|||
|
||||
export const queryTypes = gql`
|
||||
type Query {
|
||||
getBosNodeScores(publicKey: String!): [BosScore]!
|
||||
getBosScores: BosScoreResponse!
|
||||
getBaseInfo: BaseInfo!
|
||||
getBoltzSwapStatus(ids: [String]!): [BoltzSwap]!
|
||||
getBoltzInfo: BoltzInfoType!
|
||||
getLnMarketsStatus: String!
|
||||
|
@ -120,6 +123,9 @@ export const mutationTypes = gql`
|
|||
description: String
|
||||
): String!
|
||||
fetchLnUrl(url: String!): LnUrlRequest
|
||||
createBaseTokenInvoice: baseInvoiceType
|
||||
createBaseToken(id: String!): Boolean!
|
||||
deleteBaseToken: Boolean!
|
||||
createBaseInvoice(amount: Int!): baseInvoiceType
|
||||
createThunderPoints(
|
||||
id: String!
|
||||
|
|
|
@ -5,7 +5,11 @@ import { requestLimiter } from 'server/helpers/rateLimiter';
|
|||
|
||||
export const walletResolvers = {
|
||||
Query: {
|
||||
getWalletInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||
getWalletInfo: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'getWalletInfo');
|
||||
|
||||
const { lnd } = context;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { GetChannelsType } from 'server/types/ln-service.types';
|
|||
|
||||
export const getChannelReport = async (
|
||||
_: undefined,
|
||||
params: any,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'channelReport');
|
||||
|
|
|
@ -24,6 +24,7 @@ export const ContextMock: ContextType = {
|
|||
],
|
||||
res: {} as ServerResponse,
|
||||
lnMarketsAuth: 'lnMarketAuth',
|
||||
tokenAuth: 'tokenAuth',
|
||||
};
|
||||
|
||||
export const ContextMockNoAccounts: ContextType = {
|
||||
|
@ -39,6 +40,7 @@ export const ContextMockNoAccounts: ContextType = {
|
|||
accounts: [],
|
||||
res: {} as ServerResponse,
|
||||
lnMarketsAuth: 'lnMarketAuth',
|
||||
tokenAuth: 'tokenAuth',
|
||||
};
|
||||
|
||||
export const ContextMockNoSSO: ContextType = {
|
||||
|
@ -60,4 +62,5 @@ export const ContextMockNoSSO: ContextType = {
|
|||
],
|
||||
res: {} as ServerResponse,
|
||||
lnMarketsAuth: 'lnMarketAuth',
|
||||
tokenAuth: 'tokenAuth',
|
||||
};
|
||||
|
|
|
@ -17,4 +17,5 @@ export type ContextType = {
|
|||
accounts: ParsedAccount[];
|
||||
res: ServerResponse;
|
||||
lnMarketsAuth: string | null;
|
||||
tokenAuth: string | null;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export const appConstants = {
|
||||
cookieName: 'Thub-Auth',
|
||||
lnMarketsAuth: 'LnMarkets-Auth',
|
||||
tokenCookieName: 'Tbase-Auth',
|
||||
};
|
||||
|
|
57
src/context/BaseContext.tsx
Normal file
57
src/context/BaseContext.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React, { createContext, useContext, useReducer } from 'react';
|
||||
|
||||
type State = {
|
||||
hasToken: boolean;
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type: 'change';
|
||||
hasToken: boolean;
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
export const StateContext = createContext<State | undefined>(undefined);
|
||||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'change':
|
||||
return { hasToken: action.hasToken };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const BaseProvider: React.FC<{ initialHasToken: boolean }> = ({
|
||||
children,
|
||||
initialHasToken = false,
|
||||
}) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, {
|
||||
hasToken: initialHasToken,
|
||||
});
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useBaseState = () => {
|
||||
const context = useContext(StateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useBaseState must be used within a BaseProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
const useBaseDispatch = () => {
|
||||
const context = useContext(DispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useBaseDispatch must be used within a BaseProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { BaseProvider, useBaseState, useBaseDispatch };
|
46
src/graphql/mutations/__generated__/createBaseToken.generated.tsx
generated
Normal file
46
src/graphql/mutations/__generated__/createBaseToken.generated.tsx
generated
Normal file
|
@ -0,0 +1,46 @@
|
|||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
export type CreateBaseTokenMutationVariables = Types.Exact<{
|
||||
id: Types.Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateBaseTokenMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& Pick<Types.Mutation, 'createBaseToken'>
|
||||
);
|
||||
|
||||
|
||||
export const CreateBaseTokenDocument = gql`
|
||||
mutation CreateBaseToken($id: String!) {
|
||||
createBaseToken(id: $id)
|
||||
}
|
||||
`;
|
||||
export type CreateBaseTokenMutationFn = Apollo.MutationFunction<CreateBaseTokenMutation, CreateBaseTokenMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateBaseTokenMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateBaseTokenMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateBaseTokenMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createBaseTokenMutation, { data, loading, error }] = useCreateBaseTokenMutation({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateBaseTokenMutation(baseOptions?: Apollo.MutationHookOptions<CreateBaseTokenMutation, CreateBaseTokenMutationVariables>) {
|
||||
return Apollo.useMutation<CreateBaseTokenMutation, CreateBaseTokenMutationVariables>(CreateBaseTokenDocument, baseOptions);
|
||||
}
|
||||
export type CreateBaseTokenMutationHookResult = ReturnType<typeof useCreateBaseTokenMutation>;
|
||||
export type CreateBaseTokenMutationResult = Apollo.MutationResult<CreateBaseTokenMutation>;
|
||||
export type CreateBaseTokenMutationOptions = Apollo.BaseMutationOptions<CreateBaseTokenMutation, CreateBaseTokenMutationVariables>;
|
49
src/graphql/mutations/__generated__/createBaseTokenInvoice.generated.tsx
generated
Normal file
49
src/graphql/mutations/__generated__/createBaseTokenInvoice.generated.tsx
generated
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
export type CreateBaseTokenInvoiceMutationVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type CreateBaseTokenInvoiceMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { createBaseTokenInvoice?: Types.Maybe<(
|
||||
{ __typename?: 'baseInvoiceType' }
|
||||
& Pick<Types.BaseInvoiceType, 'request' | 'id'>
|
||||
)> }
|
||||
);
|
||||
|
||||
|
||||
export const CreateBaseTokenInvoiceDocument = gql`
|
||||
mutation CreateBaseTokenInvoice {
|
||||
createBaseTokenInvoice {
|
||||
request
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateBaseTokenInvoiceMutationFn = Apollo.MutationFunction<CreateBaseTokenInvoiceMutation, CreateBaseTokenInvoiceMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateBaseTokenInvoiceMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateBaseTokenInvoiceMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateBaseTokenInvoiceMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createBaseTokenInvoiceMutation, { data, loading, error }] = useCreateBaseTokenInvoiceMutation({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateBaseTokenInvoiceMutation(baseOptions?: Apollo.MutationHookOptions<CreateBaseTokenInvoiceMutation, CreateBaseTokenInvoiceMutationVariables>) {
|
||||
return Apollo.useMutation<CreateBaseTokenInvoiceMutation, CreateBaseTokenInvoiceMutationVariables>(CreateBaseTokenInvoiceDocument, baseOptions);
|
||||
}
|
||||
export type CreateBaseTokenInvoiceMutationHookResult = ReturnType<typeof useCreateBaseTokenInvoiceMutation>;
|
||||
export type CreateBaseTokenInvoiceMutationResult = Apollo.MutationResult<CreateBaseTokenInvoiceMutation>;
|
||||
export type CreateBaseTokenInvoiceMutationOptions = Apollo.BaseMutationOptions<CreateBaseTokenInvoiceMutation, CreateBaseTokenInvoiceMutationVariables>;
|
43
src/graphql/mutations/__generated__/deleteBaseToken.generated.tsx
generated
Normal file
43
src/graphql/mutations/__generated__/deleteBaseToken.generated.tsx
generated
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
export type DeleteBaseTokenMutationVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type DeleteBaseTokenMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& Pick<Types.Mutation, 'deleteBaseToken'>
|
||||
);
|
||||
|
||||
|
||||
export const DeleteBaseTokenDocument = gql`
|
||||
mutation DeleteBaseToken {
|
||||
deleteBaseToken
|
||||
}
|
||||
`;
|
||||
export type DeleteBaseTokenMutationFn = Apollo.MutationFunction<DeleteBaseTokenMutation, DeleteBaseTokenMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useDeleteBaseTokenMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDeleteBaseTokenMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDeleteBaseTokenMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [deleteBaseTokenMutation, { data, loading, error }] = useDeleteBaseTokenMutation({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDeleteBaseTokenMutation(baseOptions?: Apollo.MutationHookOptions<DeleteBaseTokenMutation, DeleteBaseTokenMutationVariables>) {
|
||||
return Apollo.useMutation<DeleteBaseTokenMutation, DeleteBaseTokenMutationVariables>(DeleteBaseTokenDocument, baseOptions);
|
||||
}
|
||||
export type DeleteBaseTokenMutationHookResult = ReturnType<typeof useDeleteBaseTokenMutation>;
|
||||
export type DeleteBaseTokenMutationResult = Apollo.MutationResult<DeleteBaseTokenMutation>;
|
||||
export type DeleteBaseTokenMutationOptions = Apollo.BaseMutationOptions<DeleteBaseTokenMutation, DeleteBaseTokenMutationVariables>;
|
7
src/graphql/mutations/createBaseToken.ts
Normal file
7
src/graphql/mutations/createBaseToken.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_BASE_TOKEN = gql`
|
||||
mutation CreateBaseToken($id: String!) {
|
||||
createBaseToken(id: $id)
|
||||
}
|
||||
`;
|
10
src/graphql/mutations/createBaseTokenInvoice.ts
Normal file
10
src/graphql/mutations/createBaseTokenInvoice.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_BASE_TOKEN_INVOICE = gql`
|
||||
mutation CreateBaseTokenInvoice {
|
||||
createBaseTokenInvoice {
|
||||
request
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
7
src/graphql/mutations/deleteBaseToken.ts
Normal file
7
src/graphql/mutations/deleteBaseToken.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_BASE_TOKEN = gql`
|
||||
mutation DeleteBaseToken {
|
||||
deleteBaseToken
|
||||
}
|
||||
`;
|
51
src/graphql/queries/__generated__/getBaseInfo.generated.tsx
generated
Normal file
51
src/graphql/queries/__generated__/getBaseInfo.generated.tsx
generated
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
export type GetBaseInfoQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetBaseInfoQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { getBaseInfo: (
|
||||
{ __typename?: 'BaseInfo' }
|
||||
& Pick<Types.BaseInfo, 'lastBosUpdate' | 'apiTokenSatPrice' | 'apiTokenOriginalSatPrice'>
|
||||
) }
|
||||
);
|
||||
|
||||
|
||||
export const GetBaseInfoDocument = gql`
|
||||
query GetBaseInfo {
|
||||
getBaseInfo {
|
||||
lastBosUpdate
|
||||
apiTokenSatPrice
|
||||
apiTokenOriginalSatPrice
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetBaseInfoQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetBaseInfoQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetBaseInfoQuery` 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 } = useGetBaseInfoQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetBaseInfoQuery(baseOptions?: Apollo.QueryHookOptions<GetBaseInfoQuery, GetBaseInfoQueryVariables>) {
|
||||
return Apollo.useQuery<GetBaseInfoQuery, GetBaseInfoQueryVariables>(GetBaseInfoDocument, baseOptions);
|
||||
}
|
||||
export function useGetBaseInfoLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetBaseInfoQuery, GetBaseInfoQueryVariables>) {
|
||||
return Apollo.useLazyQuery<GetBaseInfoQuery, GetBaseInfoQueryVariables>(GetBaseInfoDocument, baseOptions);
|
||||
}
|
||||
export type GetBaseInfoQueryHookResult = ReturnType<typeof useGetBaseInfoQuery>;
|
||||
export type GetBaseInfoLazyQueryHookResult = ReturnType<typeof useGetBaseInfoLazyQuery>;
|
||||
export type GetBaseInfoQueryResult = Apollo.QueryResult<GetBaseInfoQuery, GetBaseInfoQueryVariables>;
|
56
src/graphql/queries/__generated__/getBosNodeScores.generated.tsx
generated
Normal file
56
src/graphql/queries/__generated__/getBosNodeScores.generated.tsx
generated
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
export type GetBosNodeScoresQueryVariables = Types.Exact<{
|
||||
publicKey: Types.Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetBosNodeScoresQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { getBosNodeScores: Array<Types.Maybe<(
|
||||
{ __typename?: 'BosScore' }
|
||||
& Pick<Types.BosScore, 'alias' | 'public_key' | 'score' | 'updated' | 'position'>
|
||||
)>> }
|
||||
);
|
||||
|
||||
|
||||
export const GetBosNodeScoresDocument = gql`
|
||||
query GetBosNodeScores($publicKey: String!) {
|
||||
getBosNodeScores(publicKey: $publicKey) {
|
||||
alias
|
||||
public_key
|
||||
score
|
||||
updated
|
||||
position
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetBosNodeScoresQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetBosNodeScoresQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetBosNodeScoresQuery` 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 } = useGetBosNodeScoresQuery({
|
||||
* variables: {
|
||||
* publicKey: // value for 'publicKey'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetBosNodeScoresQuery(baseOptions: Apollo.QueryHookOptions<GetBosNodeScoresQuery, GetBosNodeScoresQueryVariables>) {
|
||||
return Apollo.useQuery<GetBosNodeScoresQuery, GetBosNodeScoresQueryVariables>(GetBosNodeScoresDocument, baseOptions);
|
||||
}
|
||||
export function useGetBosNodeScoresLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetBosNodeScoresQuery, GetBosNodeScoresQueryVariables>) {
|
||||
return Apollo.useLazyQuery<GetBosNodeScoresQuery, GetBosNodeScoresQueryVariables>(GetBosNodeScoresDocument, baseOptions);
|
||||
}
|
||||
export type GetBosNodeScoresQueryHookResult = ReturnType<typeof useGetBosNodeScoresQuery>;
|
||||
export type GetBosNodeScoresLazyQueryHookResult = ReturnType<typeof useGetBosNodeScoresLazyQuery>;
|
||||
export type GetBosNodeScoresQueryResult = Apollo.QueryResult<GetBosNodeScoresQuery, GetBosNodeScoresQueryVariables>;
|
60
src/graphql/queries/__generated__/getBosScores.generated.tsx
generated
Normal file
60
src/graphql/queries/__generated__/getBosScores.generated.tsx
generated
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
export type GetBosScoresQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetBosScoresQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { getBosScores: (
|
||||
{ __typename?: 'BosScoreResponse' }
|
||||
& Pick<Types.BosScoreResponse, 'updated'>
|
||||
& { scores: Array<(
|
||||
{ __typename?: 'BosScore' }
|
||||
& Pick<Types.BosScore, 'alias' | 'public_key' | 'score' | 'updated' | 'position'>
|
||||
)> }
|
||||
) }
|
||||
);
|
||||
|
||||
|
||||
export const GetBosScoresDocument = gql`
|
||||
query GetBosScores {
|
||||
getBosScores {
|
||||
updated
|
||||
scores {
|
||||
alias
|
||||
public_key
|
||||
score
|
||||
updated
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetBosScoresQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetBosScoresQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetBosScoresQuery` 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 } = useGetBosScoresQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetBosScoresQuery(baseOptions?: Apollo.QueryHookOptions<GetBosScoresQuery, GetBosScoresQueryVariables>) {
|
||||
return Apollo.useQuery<GetBosScoresQuery, GetBosScoresQueryVariables>(GetBosScoresDocument, baseOptions);
|
||||
}
|
||||
export function useGetBosScoresLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetBosScoresQuery, GetBosScoresQueryVariables>) {
|
||||
return Apollo.useLazyQuery<GetBosScoresQuery, GetBosScoresQueryVariables>(GetBosScoresDocument, baseOptions);
|
||||
}
|
||||
export type GetBosScoresQueryHookResult = ReturnType<typeof useGetBosScoresQuery>;
|
||||
export type GetBosScoresLazyQueryHookResult = ReturnType<typeof useGetBosScoresLazyQuery>;
|
||||
export type GetBosScoresQueryResult = Apollo.QueryResult<GetBosScoresQuery, GetBosScoresQueryVariables>;
|
11
src/graphql/queries/getBaseInfo.ts
Normal file
11
src/graphql/queries/getBaseInfo.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_BASE_INFO = gql`
|
||||
query GetBaseInfo {
|
||||
getBaseInfo {
|
||||
lastBosUpdate
|
||||
apiTokenSatPrice
|
||||
apiTokenOriginalSatPrice
|
||||
}
|
||||
}
|
||||
`;
|
13
src/graphql/queries/getBosNodeScores.ts
Normal file
13
src/graphql/queries/getBosNodeScores.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_BOS_NODE_SCORES = gql`
|
||||
query GetBosNodeScores($publicKey: String!) {
|
||||
getBosNodeScores(publicKey: $publicKey) {
|
||||
alias
|
||||
public_key
|
||||
score
|
||||
updated
|
||||
position
|
||||
}
|
||||
}
|
||||
`;
|
16
src/graphql/queries/getBosScores.ts
Normal file
16
src/graphql/queries/getBosScores.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_BOS_SCORES = gql`
|
||||
query GetBosScores {
|
||||
getBosScores {
|
||||
updated
|
||||
scores {
|
||||
alias
|
||||
public_key
|
||||
score
|
||||
updated
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -1,6 +1,8 @@
|
|||
/* eslint-disable */
|
||||
export type Maybe<T> = T | null;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
ID: string;
|
||||
|
@ -41,6 +43,9 @@ export type PermissionsType = {
|
|||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
getBosNodeScores: Array<Maybe<BosScore>>;
|
||||
getBosScores: BosScoreResponse;
|
||||
getBaseInfo: BaseInfo;
|
||||
getBoltzSwapStatus: Array<Maybe<BoltzSwap>>;
|
||||
getBoltzInfo: BoltzInfoType;
|
||||
getLnMarketsStatus: Scalars['String'];
|
||||
|
@ -89,6 +94,11 @@ export type Query = {
|
|||
};
|
||||
|
||||
|
||||
export type QueryGetBosNodeScoresArgs = {
|
||||
publicKey: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetBoltzSwapStatusArgs = {
|
||||
ids: Array<Maybe<Scalars['String']>>;
|
||||
};
|
||||
|
@ -211,6 +221,9 @@ export type Mutation = {
|
|||
lnUrlPay: PaySuccess;
|
||||
lnUrlWithdraw: Scalars['String'];
|
||||
fetchLnUrl?: Maybe<LnUrlRequest>;
|
||||
createBaseTokenInvoice?: Maybe<BaseInvoiceType>;
|
||||
createBaseToken: Scalars['Boolean'];
|
||||
deleteBaseToken: Scalars['Boolean'];
|
||||
createBaseInvoice?: Maybe<BaseInvoiceType>;
|
||||
createThunderPoints: Scalars['Boolean'];
|
||||
closeChannel?: Maybe<CloseChannelType>;
|
||||
|
@ -295,6 +308,11 @@ export type MutationFetchLnUrlArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationCreateBaseTokenArgs = {
|
||||
id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateBaseInvoiceArgs = {
|
||||
amount: Scalars['Int'];
|
||||
};
|
||||
|
@ -1059,6 +1077,28 @@ export type BaseInvoiceType = {
|
|||
request: Scalars['String'];
|
||||
};
|
||||
|
||||
export type BosScore = {
|
||||
__typename?: 'BosScore';
|
||||
alias: Scalars['String'];
|
||||
public_key: Scalars['String'];
|
||||
score: Scalars['Int'];
|
||||
updated: Scalars['String'];
|
||||
position: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type BosScoreResponse = {
|
||||
__typename?: 'BosScoreResponse';
|
||||
updated: Scalars['String'];
|
||||
scores: Array<BosScore>;
|
||||
};
|
||||
|
||||
export type BaseInfo = {
|
||||
__typename?: 'BaseInfo';
|
||||
lastBosUpdate: Scalars['String'];
|
||||
apiTokenSatPrice: Scalars['Int'];
|
||||
apiTokenOriginalSatPrice: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type WithdrawRequest = {
|
||||
__typename?: 'WithdrawRequest';
|
||||
callback?: Maybe<Scalars['String']>;
|
||||
|
|
|
@ -4,7 +4,10 @@ import { useGetBaseCanConnectQuery } from 'src/graphql/queries/__generated__/get
|
|||
export const useBaseConnect = () => {
|
||||
const [canConnect, setCanConnect] = useState<boolean>(false);
|
||||
|
||||
const { loading, error, data } = useGetBaseCanConnectQuery({ ssr: false });
|
||||
const { loading, error, data } = useGetBaseCanConnectQuery({
|
||||
ssr: false,
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data?.getBaseCanConnect || error) return;
|
||||
|
|
|
@ -19,6 +19,7 @@ type StatusState = {
|
|||
pendingChannelCount: number;
|
||||
closedChannelCount: number;
|
||||
peersCount: number;
|
||||
publicKey: string;
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
|
@ -37,6 +38,7 @@ const initialState = {
|
|||
pendingChannelCount: 0,
|
||||
closedChannelCount: 0,
|
||||
peersCount: 0,
|
||||
publicKey: '',
|
||||
};
|
||||
|
||||
export const useNodeInfo = (): StatusState => {
|
||||
|
@ -64,6 +66,7 @@ export const useNodeInfo = (): StatusState => {
|
|||
pending_channels_count,
|
||||
closed_channels_count,
|
||||
peers_count,
|
||||
public_key,
|
||||
} = getNodeInfo as NodeInfoType;
|
||||
const {
|
||||
confirmedBalance,
|
||||
|
@ -88,6 +91,7 @@ export const useNodeInfo = (): StatusState => {
|
|||
pendingChannelCount: pending_channels_count,
|
||||
closedChannelCount: closed_channels_count,
|
||||
peersCount: peers_count,
|
||||
publicKey: public_key,
|
||||
});
|
||||
}, [data, error, loading]);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
Icon,
|
||||
Heart,
|
||||
Shuffle,
|
||||
Aperture,
|
||||
} from 'react-feather';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useBaseConnect } from 'src/hooks/UseBaseConnect';
|
||||
|
@ -128,6 +129,7 @@ const DONATIONS = '/leaderboard';
|
|||
const CHAT = '/chat';
|
||||
const SETTINGS = '/settings';
|
||||
const SWAP = '/swap';
|
||||
const SCORES = '/scores';
|
||||
|
||||
interface NavigationProps {
|
||||
isBurger?: boolean;
|
||||
|
@ -181,6 +183,7 @@ export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
|||
{renderNavButton('Tools', TOOLS, Shield, sidebar)}
|
||||
{renderNavButton('Swap', SWAP, Shuffle, sidebar)}
|
||||
{renderNavButton('Stats', STATS, BarChart2, sidebar)}
|
||||
{connected && renderNavButton('Scores', SCORES, Aperture)}
|
||||
</ButtonSection>
|
||||
);
|
||||
|
||||
|
@ -197,6 +200,7 @@ export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
|||
{renderBurgerNav('Tools', TOOLS, Shield)}
|
||||
{renderBurgerNav('Swap', SWAP, Shuffle)}
|
||||
{renderBurgerNav('Stats', STATS, BarChart2)}
|
||||
{connected && renderBurgerNav('Scores', SCORES, Aperture)}
|
||||
{connected && renderBurgerNav('Donations', DONATIONS, Heart)}
|
||||
{renderBurgerNav('Chat', CHAT, MessageCircle)}
|
||||
{renderBurgerNav('Settings', SETTINGS, Settings)}
|
||||
|
|
|
@ -8,22 +8,25 @@ import { GET_AUTH_TOKEN } from 'src/graphql/mutations/getAuthToken';
|
|||
const cookieProps = (
|
||||
context: NextPageContext,
|
||||
noAuth?: boolean
|
||||
): { theme: string; authenticated: boolean } => {
|
||||
if (!context?.req) return { theme: 'dark', authenticated: false };
|
||||
): { theme: string; authenticated: boolean; hasToken: boolean } => {
|
||||
if (!context?.req)
|
||||
return { theme: 'dark', authenticated: false, hasToken: false };
|
||||
|
||||
const cookies = parseCookies(context.req);
|
||||
|
||||
const hasToken = !!cookies[appConstants.tokenCookieName];
|
||||
|
||||
if (!cookies[appConstants.cookieName] && !noAuth) {
|
||||
context.res?.writeHead(302, { Location: '/login' });
|
||||
context.res?.end();
|
||||
|
||||
return { theme: 'dark', authenticated: false };
|
||||
return { theme: 'dark', authenticated: false, hasToken };
|
||||
}
|
||||
|
||||
if (cookies?.theme) {
|
||||
return { theme: cookies.theme, authenticated: true };
|
||||
return { theme: cookies.theme, authenticated: true, hasToken };
|
||||
}
|
||||
return { theme: 'dark', authenticated: true };
|
||||
return { theme: 'dark', authenticated: true, hasToken };
|
||||
};
|
||||
|
||||
type QueryProps = {
|
||||
|
@ -54,7 +57,7 @@ export const getProps = async (
|
|||
});
|
||||
}
|
||||
|
||||
const { theme, authenticated } = cookieProps(context, noAuth);
|
||||
const { theme, authenticated, hasToken } = cookieProps(context, noAuth);
|
||||
|
||||
const apolloClient = initializeApollo(undefined, context);
|
||||
|
||||
|
@ -76,13 +79,14 @@ export const getProps = async (
|
|||
}
|
||||
}
|
||||
} else {
|
||||
return { props: { initialConfig: { theme } } };
|
||||
return { props: { initialConfig: { theme }, hasToken } };
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialApolloState: apolloClient.cache.extract(),
|
||||
initialConfig: { theme },
|
||||
hasToken,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
202
src/views/scores/AreaGraph.tsx
Normal file
202
src/views/scores/AreaGraph.tsx
Normal file
|
@ -0,0 +1,202 @@
|
|||
import React, { useMemo, useCallback } from 'react';
|
||||
import { AreaClosed, Line, Bar } from '@visx/shape';
|
||||
import { curveMonotoneX } from '@visx/curve';
|
||||
import { scaleTime, scaleLinear } from '@visx/scale';
|
||||
import {
|
||||
withTooltip,
|
||||
Tooltip,
|
||||
TooltipWithBounds,
|
||||
defaultStyles,
|
||||
} from '@visx/tooltip';
|
||||
import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withTooltip';
|
||||
import { localPoint } from '@visx/event';
|
||||
import { max, extent, bisector } from 'd3-array';
|
||||
import { chartColors, themeColors } from 'src/styles/Themes';
|
||||
import format from 'date-fns/format';
|
||||
|
||||
const tooltipStyles = {
|
||||
...defaultStyles,
|
||||
background: themeColors.blue7,
|
||||
border: '1px solid white',
|
||||
color: 'white',
|
||||
};
|
||||
|
||||
const getDate = (d: DataType) => new Date(d.date);
|
||||
const getValue = (d: DataType) => d.value;
|
||||
const bisectDate = bisector<DataType, Date>(d => new Date(d.date)).left;
|
||||
|
||||
type DataType = {
|
||||
date: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type AreaProps = {
|
||||
data: DataType[];
|
||||
areaColor?: string;
|
||||
lineColor?: string;
|
||||
tooltipText?: string;
|
||||
clickCallback?: () => void;
|
||||
width: number;
|
||||
height: number;
|
||||
margin?: { top: number; right: number; bottom: number; left: number };
|
||||
};
|
||||
|
||||
export const AreaGraph = withTooltip<AreaProps, DataType>(
|
||||
({
|
||||
data,
|
||||
areaColor = chartColors.orange,
|
||||
lineColor = themeColors.blue2,
|
||||
tooltipText,
|
||||
clickCallback,
|
||||
width,
|
||||
height,
|
||||
margin = { top: 0, right: 0, bottom: 0, left: 0 },
|
||||
showTooltip,
|
||||
hideTooltip,
|
||||
tooltipData,
|
||||
tooltipTop = 0,
|
||||
tooltipLeft = 0,
|
||||
}: AreaProps & WithTooltipProvidedProps<DataType>) => {
|
||||
if (width < 10) return null;
|
||||
|
||||
const innerWidth = width - margin.left - margin.right;
|
||||
const innerHeight = height - margin.top - margin.bottom;
|
||||
|
||||
const dateScale = useMemo(
|
||||
() =>
|
||||
scaleTime({
|
||||
range: [margin.left, innerWidth + margin.left],
|
||||
domain: extent(data, getDate) as [Date, Date],
|
||||
}),
|
||||
[innerWidth, margin.left, data]
|
||||
);
|
||||
|
||||
const valueScale = useMemo(
|
||||
() =>
|
||||
scaleLinear({
|
||||
range: [innerHeight + margin.top, margin.top],
|
||||
domain: [0, max(data, getValue) || 0],
|
||||
nice: false,
|
||||
}),
|
||||
[margin.top, innerHeight, data]
|
||||
);
|
||||
|
||||
const handleTooltip = useCallback(
|
||||
(
|
||||
event:
|
||||
| React.TouchEvent<SVGRectElement>
|
||||
| React.MouseEvent<SVGRectElement>
|
||||
) => {
|
||||
const { x } = localPoint(event) || { x: 0 };
|
||||
const x0 = dateScale.invert(x);
|
||||
|
||||
const index = bisectDate(data, x0, 1);
|
||||
|
||||
const d0 = data[index - 1];
|
||||
const d1 = data[index];
|
||||
let d = d0;
|
||||
|
||||
if (d1 && getDate(d1)) {
|
||||
const firstPart = x0.valueOf() - getDate(d0).valueOf();
|
||||
const secondPart = getDate(d1).valueOf() - x0.valueOf();
|
||||
|
||||
d = firstPart > secondPart ? d1 : d0;
|
||||
}
|
||||
|
||||
showTooltip({
|
||||
tooltipData: d,
|
||||
tooltipLeft: x,
|
||||
tooltipTop: valueScale(getValue(d)),
|
||||
});
|
||||
},
|
||||
[showTooltip, valueScale, dateScale, data]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<svg width={width} height={height}>
|
||||
<AreaClosed<DataType>
|
||||
data={data}
|
||||
x={d => dateScale(getDate(d)) ?? 0}
|
||||
y={d => valueScale(getValue(d)) ?? 0}
|
||||
yScale={valueScale}
|
||||
strokeWidth={1}
|
||||
stroke={areaColor}
|
||||
fill={areaColor}
|
||||
curve={curveMonotoneX}
|
||||
/>
|
||||
<Bar
|
||||
x={margin.left}
|
||||
y={margin.top}
|
||||
width={innerWidth}
|
||||
height={innerHeight}
|
||||
fill="transparent"
|
||||
rx={14}
|
||||
onTouchStart={handleTooltip}
|
||||
onTouchMove={handleTooltip}
|
||||
onMouseMove={handleTooltip}
|
||||
onMouseLeave={() => hideTooltip()}
|
||||
onClick={() => {
|
||||
clickCallback && clickCallback();
|
||||
}}
|
||||
/>
|
||||
{tooltipData && (
|
||||
<g>
|
||||
<Line
|
||||
from={{ x: tooltipLeft, y: margin.top }}
|
||||
to={{ x: tooltipLeft, y: innerHeight + margin.top }}
|
||||
stroke={lineColor}
|
||||
strokeWidth={2}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<circle
|
||||
cx={tooltipLeft}
|
||||
cy={tooltipTop + 1}
|
||||
r={4}
|
||||
fill="black"
|
||||
fillOpacity={0.1}
|
||||
stroke="black"
|
||||
strokeOpacity={0.1}
|
||||
strokeWidth={2}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<circle
|
||||
cx={tooltipLeft}
|
||||
cy={tooltipTop}
|
||||
r={4}
|
||||
fill={lineColor}
|
||||
stroke="white"
|
||||
strokeWidth={2}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
</g>
|
||||
)}
|
||||
</svg>
|
||||
{tooltipData && (
|
||||
<div>
|
||||
<TooltipWithBounds
|
||||
key={Math.random()}
|
||||
top={tooltipTop - 12}
|
||||
left={tooltipLeft + 12}
|
||||
style={tooltipStyles}
|
||||
>
|
||||
{`${tooltipText}${getValue(tooltipData)}`}
|
||||
</TooltipWithBounds>
|
||||
<Tooltip
|
||||
top={innerHeight + margin.top - 14}
|
||||
left={tooltipLeft}
|
||||
style={{
|
||||
...defaultStyles,
|
||||
minWidth: 72,
|
||||
textAlign: 'center',
|
||||
transform: 'translateX(-50%)',
|
||||
}}
|
||||
>
|
||||
{format(getDate(tooltipData), 'LLL d, yyyy')}
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
70
src/views/scores/NodeGraph.tsx
Normal file
70
src/views/scores/NodeGraph.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { ParentSize } from '@visx/responsive';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import { useBaseState } from 'src/context/BaseContext';
|
||||
import { useGetBosNodeScoresQuery } from 'src/graphql/queries/__generated__/getBosNodeScores.generated';
|
||||
import { chartColors, mediaWidths, themeColors } from 'src/styles/Themes';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
import styled from 'styled-components';
|
||||
import { isArray } from 'underscore';
|
||||
import { AreaGraph } from './AreaGraph';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
height: 160px;
|
||||
width: 100%;
|
||||
margin: 32px 0;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
height: 80px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Graph = () => {
|
||||
const [isPos, setIsPos] = useState<boolean>(false);
|
||||
const { hasToken } = useBaseState();
|
||||
const { query } = useRouter();
|
||||
const { id } = query;
|
||||
|
||||
const toggle = () => setIsPos(p => !p);
|
||||
|
||||
const publicKey = (isArray(id) ? id[0] : id) || '';
|
||||
|
||||
const { data, loading } = useGetBosNodeScoresQuery({
|
||||
skip: !hasToken,
|
||||
variables: { publicKey },
|
||||
fetchPolicy: 'cache-first',
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return <LoadingCard noCard={true} />;
|
||||
}
|
||||
|
||||
const scores = data?.getBosNodeScores || [];
|
||||
const final = scores
|
||||
.map(s => ({
|
||||
date: s?.updated || '',
|
||||
value: (isPos ? s?.position : s?.score) || 0,
|
||||
}))
|
||||
.reverse();
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<ParentSize>
|
||||
{parent => (
|
||||
<AreaGraph
|
||||
data={final}
|
||||
width={parent.width}
|
||||
height={parent.height}
|
||||
clickCallback={toggle}
|
||||
areaColor={isPos ? themeColors.blue2 : chartColors.orange}
|
||||
lineColor={isPos ? chartColors.orange : themeColors.blue2}
|
||||
tooltipText={isPos ? 'Position:' : 'Score:'}
|
||||
/>
|
||||
)}
|
||||
</ParentSize>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
68
src/views/scores/NodeInfo.tsx
Normal file
68
src/views/scores/NodeInfo.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
import React from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGetNodeQuery } from 'src/graphql/queries/__generated__/getNode.generated';
|
||||
import { isArray } from 'underscore';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
DarkSubTitle,
|
||||
SubTitle,
|
||||
} from 'src/components/generic/Styled';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import {
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
renderLine,
|
||||
} from 'src/components/generic/helpers';
|
||||
import { Price } from 'src/components/price/Price';
|
||||
import { useBaseState } from 'src/context/BaseContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
|
||||
export const NodeInfo = () => {
|
||||
const { hasToken } = useBaseState();
|
||||
const { query } = useRouter();
|
||||
const { id } = query;
|
||||
|
||||
const publicKey = (isArray(id) ? id[0] : id) || '';
|
||||
|
||||
const { data, loading } = useGetNodeQuery({
|
||||
skip: !hasToken,
|
||||
variables: { publicKey },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
if (!hasToken) {
|
||||
return <LoadingCard noTitle={true} />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <LoadingCard title={'Node Info'} />;
|
||||
}
|
||||
|
||||
if (!data?.getNode.node || data?.getNode?.node?.alias === 'Node not found') {
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>Node Info</SubTitle>
|
||||
<Card>
|
||||
<DarkSubTitle>Node not found</DarkSubTitle>
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
}
|
||||
|
||||
const { alias, channel_count, capacity, updated_at } = data.getNode.node;
|
||||
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>Node Info</SubTitle>
|
||||
<Card>
|
||||
<SubTitle>{alias}</SubTitle>
|
||||
{renderLine('Channel Count', channel_count)}
|
||||
{renderLine('Capacity', <Price amount={capacity} />)}
|
||||
{renderLine('Last Update', `${getDateDif(updated_at)} ago`)}
|
||||
{renderLine('Last Update Date', getFormatDate(updated_at))}
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
66
src/views/scores/NodeScores.tsx
Normal file
66
src/views/scores/NodeScores.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
import React, { FC, useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { isArray } from 'underscore';
|
||||
import { Card, CardWithTitle, SubTitle } from 'src/components/generic/Styled';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import { useBaseState } from 'src/context/BaseContext';
|
||||
import { useGetBosNodeScoresQuery } from 'src/graphql/queries/__generated__/getBosNodeScores.generated';
|
||||
import { Table } from 'src/components/table';
|
||||
import { getFormatDate } from 'src/components/generic/helpers';
|
||||
|
||||
type NodeScoresProps = {
|
||||
callback: () => void;
|
||||
errorCallback: () => void;
|
||||
};
|
||||
|
||||
export const NodeScores: FC<NodeScoresProps> = ({
|
||||
callback,
|
||||
errorCallback,
|
||||
}) => {
|
||||
const { hasToken } = useBaseState();
|
||||
const { query } = useRouter();
|
||||
const { id } = query;
|
||||
|
||||
const publicKey = (isArray(id) ? id[0] : id) || '';
|
||||
|
||||
const { data, loading, error } = useGetBosNodeScoresQuery({
|
||||
skip: !hasToken,
|
||||
variables: { publicKey },
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!error) return;
|
||||
errorCallback();
|
||||
}, [error, errorCallback]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data?.getBosNodeScores) return;
|
||||
callback();
|
||||
}, [loading, data, callback]);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingCard title={'Node Score Info'} />;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ Header: 'Date', accessor: 'date' },
|
||||
{ Header: 'Score', accessor: 'score' },
|
||||
{ Header: 'Position', accessor: 'position' },
|
||||
];
|
||||
|
||||
const finalData = data?.getBosNodeScores || [];
|
||||
const tableData = finalData.map(s => ({
|
||||
...s,
|
||||
date: getFormatDate(s?.updated),
|
||||
}));
|
||||
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>Historical Scores</SubTitle>
|
||||
<Card>
|
||||
<Table withBorder={true} tableData={tableData} tableColumns={columns} />
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
91
src/views/scores/ScoreCard.tsx
Normal file
91
src/views/scores/ScoreCard.tsx
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { useRouter } from 'next/router';
|
||||
import { FC } from 'react';
|
||||
import { getNodeLink, renderLine } from 'src/components/generic/helpers';
|
||||
import { Link } from 'src/components/link/Link';
|
||||
import { BosScore } from 'src/graphql/types';
|
||||
import { themeColors } from 'src/styles/Themes';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type ScoreCardProps = {
|
||||
score: BosScore | null;
|
||||
};
|
||||
|
||||
const getBorderColor = (index: number) => {
|
||||
switch (index) {
|
||||
case 1:
|
||||
return 'gold';
|
||||
case 2:
|
||||
return 'orange';
|
||||
case 3:
|
||||
return 'white';
|
||||
default:
|
||||
return themeColors.blue2;
|
||||
}
|
||||
};
|
||||
|
||||
const getWidth = (index: number): string => {
|
||||
switch (index) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return '2px';
|
||||
default:
|
||||
return '1px';
|
||||
}
|
||||
};
|
||||
|
||||
const S = {
|
||||
Score: styled.div<{ borderWidth?: string; borderColor?: string }>`
|
||||
padding: 8px;
|
||||
border: ${({ borderWidth }) => borderWidth || '2px'} solid
|
||||
${({ borderColor }) => borderColor || 'gold'};
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
margin: 0 0 16px;
|
||||
cursor: pointer;
|
||||
`,
|
||||
NoScore: styled.div`
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
margin: 0 0 16px;
|
||||
cursor: pointer;
|
||||
`,
|
||||
};
|
||||
|
||||
export const ScoreCard: FC<ScoreCardProps> = ({ score }) => {
|
||||
const { push } = useRouter();
|
||||
|
||||
if (!score) {
|
||||
return (
|
||||
<S.NoScore>
|
||||
<Link
|
||||
href={'https://openoms.gitbook.io/lightning-node-management/bosscore'}
|
||||
newTab={true}
|
||||
>
|
||||
This node is not in the BOS list. Read more about these scores here.
|
||||
</Link>
|
||||
</S.NoScore>
|
||||
);
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if (score.public_key) {
|
||||
push(appendBasePath(`/scores/${score.public_key}`));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<S.Score
|
||||
borderWidth={getWidth(score.position)}
|
||||
borderColor={getBorderColor(score.position)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{score.alias}
|
||||
{renderLine('Position', score.position)}
|
||||
{renderLine('Score', score.score)}
|
||||
{renderLine('Public Key', getNodeLink(score.public_key))}
|
||||
</S.Score>
|
||||
);
|
||||
};
|
55
src/views/token/BuyButton.tsx
Normal file
55
src/views/token/BuyButton.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
|
||||
import { toast } from 'react-toastify';
|
||||
import Modal from 'src/components/modal/ReactModal';
|
||||
import { useCreateBaseTokenInvoiceMutation } from 'src/graphql/mutations/__generated__/createBaseTokenInvoice.generated';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
import { RequestModal } from 'src/views/home/account/pay/RequestModal';
|
||||
import { chartColors } from 'src/styles/Themes';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
|
||||
export const BuyButton: FC<{ paidCallback: (id: string) => void }> = ({
|
||||
children,
|
||||
paidCallback,
|
||||
}) => {
|
||||
const [modalOpen, modalOpenSet] = useState<boolean>(false);
|
||||
const [invoice, invoiceSet] = useState<string>('');
|
||||
|
||||
const [buy, { data, loading }] = useCreateBaseTokenInvoiceMutation({
|
||||
onError: err => toast.error(getErrorContent(err)),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data?.createBaseTokenInvoice) return;
|
||||
invoiceSet(data.createBaseTokenInvoice.request);
|
||||
modalOpenSet(true);
|
||||
}, [loading, data]);
|
||||
|
||||
const handleReset = () => {
|
||||
invoiceSet('');
|
||||
modalOpenSet(false);
|
||||
};
|
||||
|
||||
const handlePaidReset = () => {
|
||||
invoiceSet('');
|
||||
modalOpenSet(false);
|
||||
paidCallback(data?.createBaseTokenInvoice?.id || '');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColorButton
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
withMargin={'24px 0 0'}
|
||||
fullWidth={true}
|
||||
color={chartColors.green}
|
||||
onClick={buy}
|
||||
>
|
||||
{children}
|
||||
</ColorButton>
|
||||
<Modal isOpen={modalOpen} closeCallback={handleReset}>
|
||||
<RequestModal request={invoice} handleReset={handlePaidReset} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
63
src/views/token/PaidCard.tsx
Normal file
63
src/views/token/PaidCard.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { FC, useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { renderLine } from 'src/components/generic/helpers';
|
||||
import { Card, DarkSubTitle, Separation } from 'src/components/generic/Styled';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import { useBaseDispatch } from 'src/context/BaseContext';
|
||||
import { useCreateBaseTokenMutation } from 'src/graphql/mutations/__generated__/createBaseToken.generated';
|
||||
import { chartColors } from 'src/styles/Themes';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const S = {
|
||||
title: styled.div`
|
||||
color: ${chartColors.green};
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
`,
|
||||
center: styled.div`
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
`,
|
||||
};
|
||||
|
||||
export const PaidCard: FC<{ id: string }> = ({ id }) => {
|
||||
const dispatch = useBaseDispatch();
|
||||
const [getToken, { data, loading }] = useCreateBaseTokenMutation({
|
||||
onError: err => toast.error(getErrorContent(err)),
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
getToken();
|
||||
}, [id, getToken]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data?.createBaseToken) return;
|
||||
dispatch({ type: 'change', hasToken: true });
|
||||
}, [loading, data, dispatch]);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingCard noTitle={true} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<S.title>Thank you for the purchase!</S.title>
|
||||
<Separation />
|
||||
<S.center>
|
||||
<DarkSubTitle>
|
||||
This is your payment backup id you can use to recover the token in
|
||||
case it gets deleted.
|
||||
</DarkSubTitle>
|
||||
<DarkSubTitle>
|
||||
You can also get it from the transaction in the Transaction View.
|
||||
</DarkSubTitle>
|
||||
</S.center>
|
||||
<Separation />
|
||||
{renderLine('Backup Id', id)}
|
||||
</Card>
|
||||
);
|
||||
};
|
50
src/views/token/RecoverToken.tsx
Normal file
50
src/views/token/RecoverToken.tsx
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
|
||||
import { Card, CardWithTitle, SubTitle } from 'src/components/generic/Styled';
|
||||
import { InputWithDeco } from 'src/components/input/InputWithDeco';
|
||||
import { useBaseDispatch } from 'src/context/BaseContext';
|
||||
import { useCreateBaseTokenMutation } from 'src/graphql/mutations/__generated__/createBaseToken.generated';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
|
||||
export const RecoverToken = () => {
|
||||
const { push } = useRouter();
|
||||
const [id, setId] = useState<string>('');
|
||||
|
||||
const dispatch = useBaseDispatch();
|
||||
const [getToken, { data, loading }] = useCreateBaseTokenMutation({
|
||||
onError: err => toast.error(getErrorContent(err)),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || !data?.createBaseToken) return;
|
||||
dispatch({ type: 'change', hasToken: true });
|
||||
toast.success('Succesfully recovered token');
|
||||
push(appendBasePath('/scores'));
|
||||
}, [loading, data, dispatch, push]);
|
||||
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>Recover a paid token</SubTitle>
|
||||
<Card>
|
||||
<InputWithDeco
|
||||
title={'Backup Id'}
|
||||
value={id}
|
||||
placeholder={'Transaction Id'}
|
||||
inputCallback={setId}
|
||||
/>
|
||||
<ColorButton
|
||||
loading={loading}
|
||||
disabled={loading || !id}
|
||||
fullWidth={true}
|
||||
withMargin={'16px 0 0'}
|
||||
onClick={() => getToken({ variables: { id } })}
|
||||
>
|
||||
Recover Token
|
||||
</ColorButton>
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
151
src/views/token/TokenCard.tsx
Normal file
151
src/views/token/TokenCard.tsx
Normal file
|
@ -0,0 +1,151 @@
|
|||
import React, { FC } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
DarkSubTitle,
|
||||
} from 'src/components/generic/Styled';
|
||||
import { useGetBaseInfoQuery } from 'src/graphql/queries/__generated__/getBaseInfo.generated';
|
||||
import { LifeBuoy } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
cardColor,
|
||||
chartColors,
|
||||
mediaWidths,
|
||||
themeColors,
|
||||
} from 'src/styles/Themes';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import { getPrice } from 'src/components/price/Price';
|
||||
import { usePriceState } from 'src/context/PriceContext';
|
||||
import { useConfigState } from 'src/context/ConfigContext';
|
||||
import { BuyButton } from 'src/views/token/BuyButton';
|
||||
|
||||
const S = {
|
||||
Row: styled.div`
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
Title: styled.h3`
|
||||
margin: 0 0 8px;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
`,
|
||||
Text: styled.div`
|
||||
padding-left: 16px;
|
||||
`,
|
||||
IconWrapper: styled.div`
|
||||
width: 120px;
|
||||
height: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`,
|
||||
Strike: styled.div`
|
||||
text-decoration: line-through;
|
||||
font-size: 14px;
|
||||
`,
|
||||
Price: styled.div`
|
||||
font-weight: bolder;
|
||||
font-size: 18px;
|
||||
`,
|
||||
PriceBox: styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
margin: 24px 0 0;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
Discount: styled.div`
|
||||
position: absolute;
|
||||
top: -36px;
|
||||
right: -16px;
|
||||
background: ${cardColor};
|
||||
border: 1px solid ${chartColors.green};
|
||||
color: ${chartColors.green};
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
`,
|
||||
};
|
||||
|
||||
type TokenCardProps = {
|
||||
paidCallback: (id: string) => void;
|
||||
};
|
||||
|
||||
export const TokenCard: FC<TokenCardProps> = ({ paidCallback }) => {
|
||||
const { data, loading, error } = useGetBaseInfoQuery();
|
||||
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingCard noTitle={true} />;
|
||||
}
|
||||
|
||||
if (error || !data?.getBaseInfo) {
|
||||
return (
|
||||
<Card>
|
||||
<DarkSubTitle>Unable to get token information</DarkSubTitle>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const { apiTokenSatPrice, apiTokenOriginalSatPrice } = data.getBaseInfo;
|
||||
|
||||
const monthPrice = apiTokenSatPrice * 30;
|
||||
const originalMonthPrice = apiTokenOriginalSatPrice * 30;
|
||||
|
||||
const formatDayPrice = format({ amount: apiTokenSatPrice });
|
||||
const formatPrice = format({ amount: monthPrice });
|
||||
const formatOriginalPrice = format({ amount: originalMonthPrice });
|
||||
|
||||
const percent = Math.round(
|
||||
((originalMonthPrice - monthPrice) / originalMonthPrice) * 100
|
||||
);
|
||||
|
||||
const hasDiscount = percent > 0;
|
||||
const discount = `-${percent}%`;
|
||||
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<S.Title>ThunderBase Token</S.Title>
|
||||
<Card>
|
||||
<S.Row>
|
||||
{hasDiscount && <S.Discount>{discount}</S.Discount>}
|
||||
<S.IconWrapper>
|
||||
<LifeBuoy size={64} color={themeColors.blue2} />
|
||||
</S.IconWrapper>
|
||||
<S.Text>
|
||||
This token gives you access to the full ThunderBase API.
|
||||
<DarkSubTitle>
|
||||
Features: Historical BOS score data, more to come...
|
||||
</DarkSubTitle>
|
||||
</S.Text>
|
||||
<S.PriceBox>
|
||||
{hasDiscount && (
|
||||
<S.Strike>{`${formatOriginalPrice}/month`}</S.Strike>
|
||||
)}
|
||||
<S.Price>{`${formatPrice}/month`}</S.Price>
|
||||
<DarkSubTitle>{`${formatDayPrice}/day`}</DarkSubTitle>
|
||||
</S.PriceBox>
|
||||
</S.Row>
|
||||
<BuyButton
|
||||
paidCallback={paidCallback}
|
||||
>{`Buy a 1 month token for ${formatPrice}`}</BuyButton>
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
|
@ -5,6 +5,8 @@
|
|||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
|
|
Loading…
Add table
Reference in a new issue