mirror of
https://github.com/apotdevin/thunderhub.git
synced 2024-11-19 01:40:03 +01:00
chore: lightning address (#345)
This commit is contained in:
parent
0a1f39e637
commit
1afae70edb
@ -111,6 +111,15 @@ const getBosScoresQuery = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
const getLightningAddresses = gql`
|
||||
query GetLightningAddresses {
|
||||
getLightningAddresses {
|
||||
pubkey
|
||||
lightning_address
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const ambossResolvers = {
|
||||
Query: {
|
||||
getAmbossUser: async (
|
||||
@ -199,6 +208,27 @@ export const ambossResolvers = {
|
||||
|
||||
return data.getBosScores;
|
||||
},
|
||||
getLightningAddresses: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
{ ip }: ContextType
|
||||
) => {
|
||||
await requestLimiter(ip, 'getLightningAddresses');
|
||||
|
||||
const { data, error } = await graphqlFetchWithProxy(
|
||||
appUrls.amboss,
|
||||
print(getLightningAddresses)
|
||||
);
|
||||
|
||||
if (!data?.getLightningAddresses || error) {
|
||||
if (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
throw new Error('Error getting Lightning Addresses from Amboss');
|
||||
}
|
||||
|
||||
return data.getLightningAddresses;
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
loginAmboss: async (
|
||||
|
@ -29,4 +29,9 @@ export const ambossTypes = gql`
|
||||
info: BosScoreInfo!
|
||||
scores: [BosScore!]!
|
||||
}
|
||||
|
||||
type LightningAddress {
|
||||
pubkey: String!
|
||||
lightning_address: String!
|
||||
}
|
||||
`;
|
||||
|
@ -66,6 +66,41 @@ type RequestType = PayRequestType | WithdrawRequestType;
|
||||
type RequestWithType = { isTypeOf: string } & RequestType;
|
||||
|
||||
export const lnUrlResolvers = {
|
||||
Query: {
|
||||
getLightningAddressInfo: async (
|
||||
_: undefined,
|
||||
{ address }: { address: string },
|
||||
{ ip }: ContextType
|
||||
) => {
|
||||
await requestLimiter(ip, 'getLightningAddressInfo');
|
||||
|
||||
const split = address.split('@');
|
||||
|
||||
if (split.length !== 2) {
|
||||
throw new Error('Invalid lightning address');
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetchWithProxy(
|
||||
`https://${split[1]}/.well-known/lnurlp/${split[0]}`
|
||||
);
|
||||
const result = await response.json();
|
||||
|
||||
let valid = true;
|
||||
if (!result.callback) valid = false;
|
||||
if (!result.maxSendable) valid = false;
|
||||
if (!result.minSendable) valid = false;
|
||||
|
||||
if (!valid) {
|
||||
throw new Error('Invalid lightning address');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new Error('Invalid lightning address');
|
||||
}
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
lnUrlAuth: async (
|
||||
_: undefined,
|
||||
|
@ -28,6 +28,8 @@ export const generalTypes = gql`
|
||||
|
||||
export const queryTypes = gql`
|
||||
type Query {
|
||||
getLightningAddressInfo(address: String!): PayRequest!
|
||||
getLightningAddresses: [LightningAddress!]!
|
||||
getAmbossLoginToken: String!
|
||||
getAmbossUser: AmbossUserType
|
||||
getNodeBalances: BalancesType!
|
||||
|
54
src/graphql/queries/__generated__/getLightningAddressInfo.generated.tsx
generated
Normal file
54
src/graphql/queries/__generated__/getLightningAddressInfo.generated.tsx
generated
Normal file
@ -0,0 +1,54 @@
|
||||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {}
|
||||
export type GetLightningAddressInfoQueryVariables = Types.Exact<{
|
||||
address: Types.Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetLightningAddressInfoQuery = { __typename?: 'Query', getLightningAddressInfo: { __typename?: 'PayRequest', callback?: Types.Maybe<string>, maxSendable?: Types.Maybe<string>, minSendable?: Types.Maybe<string>, metadata?: Types.Maybe<string>, commentAllowed?: Types.Maybe<number>, tag?: Types.Maybe<string> } };
|
||||
|
||||
|
||||
export const GetLightningAddressInfoDocument = gql`
|
||||
query GetLightningAddressInfo($address: String!) {
|
||||
getLightningAddressInfo(address: $address) {
|
||||
callback
|
||||
maxSendable
|
||||
minSendable
|
||||
metadata
|
||||
commentAllowed
|
||||
tag
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetLightningAddressInfoQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetLightningAddressInfoQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetLightningAddressInfoQuery` 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 } = useGetLightningAddressInfoQuery({
|
||||
* variables: {
|
||||
* address: // value for 'address'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetLightningAddressInfoQuery(baseOptions: Apollo.QueryHookOptions<GetLightningAddressInfoQuery, GetLightningAddressInfoQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetLightningAddressInfoQuery, GetLightningAddressInfoQueryVariables>(GetLightningAddressInfoDocument, options);
|
||||
}
|
||||
export function useGetLightningAddressInfoLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetLightningAddressInfoQuery, GetLightningAddressInfoQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetLightningAddressInfoQuery, GetLightningAddressInfoQueryVariables>(GetLightningAddressInfoDocument, options);
|
||||
}
|
||||
export type GetLightningAddressInfoQueryHookResult = ReturnType<typeof useGetLightningAddressInfoQuery>;
|
||||
export type GetLightningAddressInfoLazyQueryHookResult = ReturnType<typeof useGetLightningAddressInfoLazyQuery>;
|
||||
export type GetLightningAddressInfoQueryResult = Apollo.QueryResult<GetLightningAddressInfoQuery, GetLightningAddressInfoQueryVariables>;
|
47
src/graphql/queries/__generated__/getLightningAddresses.generated.tsx
generated
Normal file
47
src/graphql/queries/__generated__/getLightningAddresses.generated.tsx
generated
Normal file
@ -0,0 +1,47 @@
|
||||
/* eslint-disable */
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {}
|
||||
export type GetLightningAddressesQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetLightningAddressesQuery = { __typename?: 'Query', getLightningAddresses: Array<{ __typename?: 'LightningAddress', pubkey: string, lightning_address: string }> };
|
||||
|
||||
|
||||
export const GetLightningAddressesDocument = gql`
|
||||
query GetLightningAddresses {
|
||||
getLightningAddresses {
|
||||
pubkey
|
||||
lightning_address
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetLightningAddressesQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetLightningAddressesQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetLightningAddressesQuery` 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 } = useGetLightningAddressesQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetLightningAddressesQuery(baseOptions?: Apollo.QueryHookOptions<GetLightningAddressesQuery, GetLightningAddressesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetLightningAddressesQuery, GetLightningAddressesQueryVariables>(GetLightningAddressesDocument, options);
|
||||
}
|
||||
export function useGetLightningAddressesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetLightningAddressesQuery, GetLightningAddressesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetLightningAddressesQuery, GetLightningAddressesQueryVariables>(GetLightningAddressesDocument, options);
|
||||
}
|
||||
export type GetLightningAddressesQueryHookResult = ReturnType<typeof useGetLightningAddressesQuery>;
|
||||
export type GetLightningAddressesLazyQueryHookResult = ReturnType<typeof useGetLightningAddressesLazyQuery>;
|
||||
export type GetLightningAddressesQueryResult = Apollo.QueryResult<GetLightningAddressesQuery, GetLightningAddressesQueryVariables>;
|
14
src/graphql/queries/getLightningAddressInfo.ts
Normal file
14
src/graphql/queries/getLightningAddressInfo.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_LIGHTNING_ADDRESS_INFO = gql`
|
||||
query GetLightningAddressInfo($address: String!) {
|
||||
getLightningAddressInfo(address: $address) {
|
||||
callback
|
||||
maxSendable
|
||||
minSendable
|
||||
metadata
|
||||
commentAllowed
|
||||
tag
|
||||
}
|
||||
}
|
||||
`;
|
10
src/graphql/queries/getLightningAddresses.ts
Normal file
10
src/graphql/queries/getLightningAddresses.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_LIGHTNING_ADDRESSES = gql`
|
||||
query GetLightningAddresses {
|
||||
getLightningAddresses {
|
||||
pubkey
|
||||
lightning_address
|
||||
}
|
||||
}
|
||||
`;
|
@ -188,6 +188,12 @@ export type InvoiceType = {
|
||||
type: Scalars['String'];
|
||||
};
|
||||
|
||||
export type LightningAddress = {
|
||||
__typename?: 'LightningAddress';
|
||||
lightning_address: Scalars['String'];
|
||||
pubkey: Scalars['String'];
|
||||
};
|
||||
|
||||
export type LightningBalanceType = {
|
||||
__typename?: 'LightningBalanceType';
|
||||
active: Scalars['String'];
|
||||
@ -563,6 +569,8 @@ export type Query = {
|
||||
getForwards: Array<Maybe<Forward>>;
|
||||
getInvoiceStatusChange?: Maybe<Scalars['String']>;
|
||||
getLatestVersion?: Maybe<Scalars['String']>;
|
||||
getLightningAddressInfo: PayRequest;
|
||||
getLightningAddresses: Array<LightningAddress>;
|
||||
getLnMarketsStatus: Scalars['String'];
|
||||
getLnMarketsUrl: Scalars['String'];
|
||||
getLnMarketsUserInfo?: Maybe<LnMarketsUserInfo>;
|
||||
@ -651,6 +659,11 @@ export type QueryGetInvoiceStatusChangeArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetLightningAddressInfoArgs = {
|
||||
address: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetMessagesArgs = {
|
||||
initialize?: Maybe<Scalars['Boolean']>;
|
||||
lastMessage?: Maybe<Scalars['String']>;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { X, Layers, GitBranch, Command } from 'react-feather';
|
||||
import { X, Layers, GitBranch, Command, Zap } from 'react-feather';
|
||||
import {
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
@ -21,6 +21,7 @@ import { OpenChannel } from './openChannel';
|
||||
import { LnUrlCard } from './lnurl';
|
||||
import { LnMarketsCard } from './lnmarkets';
|
||||
import { AmbossCard } from './amboss/AmbossCard';
|
||||
import { LightningAddressCard } from './lightningAddress/LightningAddress';
|
||||
|
||||
export const QuickCard = styled.div`
|
||||
background: ${cardColor};
|
||||
@ -73,6 +74,8 @@ export const QuickActions = () => {
|
||||
return 'Open a Channel';
|
||||
case 'ln_url':
|
||||
return 'Use lnurl';
|
||||
case 'lightning_address':
|
||||
return 'Pay to a Lightning Address';
|
||||
default:
|
||||
return 'Quick Actions';
|
||||
}
|
||||
@ -90,6 +93,8 @@ export const QuickActions = () => {
|
||||
return <DecodeCard />;
|
||||
case 'ln_url':
|
||||
return <LnUrlCard />;
|
||||
case 'lightning_address':
|
||||
return <LightningAddressCard />;
|
||||
case 'open_channel':
|
||||
return (
|
||||
<Card>
|
||||
@ -101,6 +106,10 @@ export const QuickActions = () => {
|
||||
<QuickRow>
|
||||
<SupportCard callback={() => setOpenCard('support')} />
|
||||
<AmbossCard />
|
||||
<QuickCard onClick={() => setOpenCard('lightning_address')}>
|
||||
<Zap size={24} />
|
||||
<QuickTitle>Address</QuickTitle>
|
||||
</QuickCard>
|
||||
<QuickCard onClick={() => setOpenCard('open_channel')}>
|
||||
<GitBranch size={24} />
|
||||
<QuickTitle>Open</QuickTitle>
|
||||
|
81
src/views/home/quickActions/lightningAddress/Addresses.tsx
Normal file
81
src/views/home/quickActions/lightningAddress/Addresses.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import { useLocalStorage } from 'src/hooks/UseLocalStorage';
|
||||
import { Separation, Sub4Title } from 'src/components/generic/Styled';
|
||||
import styled from 'styled-components';
|
||||
import { cardBorderColor, subCardColor } from 'src/styles/Themes';
|
||||
import { FC } from 'react';
|
||||
import { useGetLightningAddressesQuery } from 'src/graphql/queries/__generated__/getLightningAddresses.generated';
|
||||
|
||||
const S = {
|
||||
wrapper: styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`,
|
||||
address: styled.button`
|
||||
font-size: 14px;
|
||||
padding: 4px 8px;
|
||||
margin: 2px;
|
||||
border: 1px solid ${cardBorderColor};
|
||||
background-color: ${subCardColor};
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
|
||||
:hover {
|
||||
background-color: ${cardBorderColor};
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
type AddressProps = {
|
||||
handleClick: (address: string) => void;
|
||||
};
|
||||
|
||||
export const PreviousAddresses: FC<AddressProps> = ({ handleClick }) => {
|
||||
const [savedAddresses] = useLocalStorage<string[]>(
|
||||
'saved_lightning_address',
|
||||
[]
|
||||
);
|
||||
|
||||
if (!savedAddresses.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separation />
|
||||
<Sub4Title>Previously Used Addresses:</Sub4Title>
|
||||
<S.wrapper>
|
||||
{savedAddresses.map((a, index) => (
|
||||
<S.address onClick={() => handleClick(a)} key={`${index}${a}`}>
|
||||
{a}
|
||||
</S.address>
|
||||
))}
|
||||
</S.wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const AmbossAddresses: FC<AddressProps> = ({ handleClick }) => {
|
||||
const { data, loading, error } = useGetLightningAddressesQuery();
|
||||
|
||||
if (loading || error || !data?.getLightningAddresses.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const addresses = data?.getLightningAddresses || [];
|
||||
const mapped = addresses.map(a => a.lightning_address);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separation />
|
||||
<Sub4Title>Amboss Addresses:</Sub4Title>
|
||||
<S.wrapper>
|
||||
{mapped.map((a, index) => (
|
||||
<S.address onClick={() => handleClick(a)} key={`${index}${a}`}>
|
||||
{a}
|
||||
</S.address>
|
||||
))}
|
||||
</S.wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,74 @@
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
|
||||
import { Card } from 'src/components/generic/Styled';
|
||||
import { InputWithDeco } from 'src/components/input/InputWithDeco';
|
||||
import Modal from 'src/components/modal/ReactModal';
|
||||
import { useGetLightningAddressInfoLazyQuery } from 'src/graphql/queries/__generated__/getLightningAddressInfo.generated';
|
||||
import { useLocalStorage } from 'src/hooks/UseLocalStorage';
|
||||
import { useMutationResultWithReset } from 'src/hooks/UseMutationWithReset';
|
||||
import { LnPay } from '../lnurl/LnPay';
|
||||
import { PreviousAddresses, AmbossAddresses } from './Addresses';
|
||||
|
||||
export const LightningAddressCard = () => {
|
||||
const [address, setAddress] = useState<string>('');
|
||||
const [savedAddresses, setSavedAddresses] = useLocalStorage<string[]>(
|
||||
'saved_lightning_address',
|
||||
[]
|
||||
);
|
||||
|
||||
const [getInfo, { data: _data, loading }] =
|
||||
useGetLightningAddressInfoLazyQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
onCompleted: () => {
|
||||
const filtered = savedAddresses.filter(a => a !== address);
|
||||
const final = [address, ...filtered];
|
||||
setSavedAddresses(final);
|
||||
},
|
||||
onError: ({ graphQLErrors }) => {
|
||||
const messages = graphQLErrors.map(e => (
|
||||
<div key={e.message}>{e.message}</div>
|
||||
));
|
||||
toast.error(<div>{messages}</div>);
|
||||
},
|
||||
});
|
||||
|
||||
const [data, reset] = useMutationResultWithReset(_data);
|
||||
|
||||
const handleClick = (address: string) => setAddress(address);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<InputWithDeco
|
||||
title={'Lightning Address'}
|
||||
value={address}
|
||||
inputCallback={v => setAddress(v)}
|
||||
/>
|
||||
<ColorButton
|
||||
arrow={true}
|
||||
fullWidth={true}
|
||||
loading={loading}
|
||||
disabled={!address || loading}
|
||||
withMargin={'16px 0 0'}
|
||||
onClick={() => getInfo({ variables: { address } })}
|
||||
>
|
||||
Pay
|
||||
</ColorButton>
|
||||
<PreviousAddresses handleClick={handleClick} />
|
||||
<AmbossAddresses handleClick={handleClick} />
|
||||
</Card>
|
||||
<Modal
|
||||
isOpen={!!data?.getLightningAddressInfo}
|
||||
closeCallback={() => {
|
||||
setAddress('');
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
{data?.getLightningAddressInfo ? (
|
||||
<LnPay request={data?.getLightningAddressInfo} />
|
||||
) : null}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user