mirror of
https://github.com/apotdevin/thunderhub.git
synced 2024-11-19 01:40:03 +01:00
chore: ghost button (#596)
This commit is contained in:
parent
e0498a6c91
commit
5aad9670cf
10
schema.gql
10
schema.gql
@ -52,6 +52,7 @@ type AmbossSubscription {
|
||||
|
||||
type AmbossUser {
|
||||
backups: UserBackupInfo!
|
||||
ghost: UserGhostInfo!
|
||||
subscription: AmbossSubscription!
|
||||
}
|
||||
|
||||
@ -263,6 +264,10 @@ type ChannelsTimeHealth {
|
||||
score: Float
|
||||
}
|
||||
|
||||
type ClaimGhostAddress {
|
||||
username: String!
|
||||
}
|
||||
|
||||
type ClosedChannel {
|
||||
capacity: Float!
|
||||
channel_age: Float
|
||||
@ -485,6 +490,7 @@ type Mutation {
|
||||
addPeer(isTemporary: Boolean, publicKey: String, socket: String, url: String): Boolean!
|
||||
bosRebalance(avoid: [String!], in_through: String, max_fee: Float, max_fee_rate: Float, max_rebalance: Float, node: String, out_inbound: Float, out_through: String, timeout_minutes: Float): BosRebalanceResult!
|
||||
claimBoltzTransaction(destination: String!, fee: Float!, preimage: String!, privateKey: String!, redeem: String!, transaction: String!): String!
|
||||
claimGhostAddress(address: String): ClaimGhostAddress!
|
||||
closeChannel(forceClose: Boolean, id: String!, targetConfirmations: Float, tokensPerVByte: Float): OpenOrCloseChannel!
|
||||
createAddress(type: String! = "p2tr"): String!
|
||||
createBaseInvoice(amount: Float!): BaseInvoice!
|
||||
@ -843,6 +849,10 @@ type UserBackupInfo {
|
||||
total_size_saved: String!
|
||||
}
|
||||
|
||||
type UserGhostInfo {
|
||||
username: String
|
||||
}
|
||||
|
||||
type Utxo {
|
||||
address: String!
|
||||
address_format: String!
|
||||
|
@ -9,6 +9,7 @@ import { SectionTitle, Text } from '../../src/components/typography/Styled';
|
||||
import { Healthchecks } from '../../src/views/amboss/Healthchecks';
|
||||
import { Balances } from '../../src/views/amboss/Balances';
|
||||
import { Billboard } from '../../src/views/amboss/Billboard';
|
||||
import { Ghost } from '../../src/views/amboss/Ghost';
|
||||
|
||||
const AmbossView = () => (
|
||||
<>
|
||||
@ -28,6 +29,7 @@ const AmbossView = () => (
|
||||
const Wrapped = () => (
|
||||
<GridWrapper>
|
||||
<AmbossView />
|
||||
<Ghost />
|
||||
<Backups />
|
||||
<Healthchecks />
|
||||
<Balances />
|
||||
|
@ -73,7 +73,7 @@ export const Link: React.FC<LinkProps> = ({
|
||||
<CorrectLink
|
||||
href={href}
|
||||
{...props}
|
||||
{...(newTab && { target: '_blank', rel: 'noreferrer' })}
|
||||
{...(newTab && { target: '_blank', rel: 'noreferrer noopener' })}
|
||||
>
|
||||
{children}
|
||||
</CorrectLink>
|
||||
|
21
src/client/src/components/logo/GhostIcon.tsx
Normal file
21
src/client/src/components/logo/GhostIcon.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
export const GhostLogo = forwardRef<any, any>(
|
||||
({ color = 'currentColor', size = 100, children, ...rest }, ref) => {
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
fill={color}
|
||||
viewBox="0 0 100 100"
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
<path d="M50 1C22.9 1 1 22.9 1 50v36.8C1 93.5 6.5 99 13.2 99s12.3-5.5 12.3-12.3C25.5 93.5 31 99 37.7 99 44.5 99 50 93.5 50 86.8 50 93.5 55.5 99 62.3 99c6.8 0 12.3-5.5 12.3-12.3C74.5 93.5 80 99 86.8 99 93.5 99 99 93.5 99 86.8V50C99 22.9 77.1 1 50 1z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
GhostLogo.displayName = 'GhostLogo';
|
64
src/client/src/graphql/mutations/__generated__/claimGhostAddress.generated.tsx
generated
Normal file
64
src/client/src/graphql/mutations/__generated__/claimGhostAddress.generated.tsx
generated
Normal file
@ -0,0 +1,64 @@
|
||||
import * as Types from '../../types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ClaimGhostAddressMutationVariables = Types.Exact<{
|
||||
address?: Types.InputMaybe<Types.Scalars['String']['input']>;
|
||||
}>;
|
||||
|
||||
export type ClaimGhostAddressMutation = {
|
||||
__typename?: 'Mutation';
|
||||
claimGhostAddress: { __typename?: 'ClaimGhostAddress'; username: string };
|
||||
};
|
||||
|
||||
export const ClaimGhostAddressDocument = gql`
|
||||
mutation ClaimGhostAddress($address: String) {
|
||||
claimGhostAddress(address: $address) {
|
||||
username
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type ClaimGhostAddressMutationFn = Apollo.MutationFunction<
|
||||
ClaimGhostAddressMutation,
|
||||
ClaimGhostAddressMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useClaimGhostAddressMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useClaimGhostAddressMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useClaimGhostAddressMutation` 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 [claimGhostAddressMutation, { data, loading, error }] = useClaimGhostAddressMutation({
|
||||
* variables: {
|
||||
* address: // value for 'address'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useClaimGhostAddressMutation(
|
||||
baseOptions?: Apollo.MutationHookOptions<
|
||||
ClaimGhostAddressMutation,
|
||||
ClaimGhostAddressMutationVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useMutation<
|
||||
ClaimGhostAddressMutation,
|
||||
ClaimGhostAddressMutationVariables
|
||||
>(ClaimGhostAddressDocument, options);
|
||||
}
|
||||
export type ClaimGhostAddressMutationHookResult = ReturnType<
|
||||
typeof useClaimGhostAddressMutation
|
||||
>;
|
||||
export type ClaimGhostAddressMutationResult =
|
||||
Apollo.MutationResult<ClaimGhostAddressMutation>;
|
||||
export type ClaimGhostAddressMutationOptions = Apollo.BaseMutationOptions<
|
||||
ClaimGhostAddressMutation,
|
||||
ClaimGhostAddressMutationVariables
|
||||
>;
|
9
src/client/src/graphql/mutations/claimGhostAddress.ts
Normal file
9
src/client/src/graphql/mutations/claimGhostAddress.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CLAIM_GHOST_ADDRESS = gql`
|
||||
mutation ClaimGhostAddress($address: String) {
|
||||
claimGhostAddress(address: $address) {
|
||||
username
|
||||
}
|
||||
}
|
||||
`;
|
@ -23,6 +23,7 @@ export type GetAmbossUserQuery = {
|
||||
available_size: string;
|
||||
remaining_size: string;
|
||||
};
|
||||
ghost: { __typename?: 'UserGhostInfo'; username?: string | null };
|
||||
} | null;
|
||||
};
|
||||
|
||||
@ -41,6 +42,9 @@ export const GetAmbossUserDocument = gql`
|
||||
available_size
|
||||
remaining_size
|
||||
}
|
||||
ghost {
|
||||
username
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -15,6 +15,9 @@ export const GET_AMBOSS_USER = gql`
|
||||
available_size
|
||||
remaining_size
|
||||
}
|
||||
ghost {
|
||||
username
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -83,6 +83,7 @@ export type AmbossSubscription = {
|
||||
export type AmbossUser = {
|
||||
__typename?: 'AmbossUser';
|
||||
backups: UserBackupInfo;
|
||||
ghost: UserGhostInfo;
|
||||
subscription: AmbossSubscription;
|
||||
};
|
||||
|
||||
@ -321,6 +322,11 @@ export type ChannelsTimeHealth = {
|
||||
score?: Maybe<Scalars['Float']['output']>;
|
||||
};
|
||||
|
||||
export type ClaimGhostAddress = {
|
||||
__typename?: 'ClaimGhostAddress';
|
||||
username: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type ClosedChannel = {
|
||||
__typename?: 'ClosedChannel';
|
||||
capacity: Scalars['Float']['output'];
|
||||
@ -565,6 +571,7 @@ export type Mutation = {
|
||||
addPeer: Scalars['Boolean']['output'];
|
||||
bosRebalance: BosRebalanceResult;
|
||||
claimBoltzTransaction: Scalars['String']['output'];
|
||||
claimGhostAddress: ClaimGhostAddress;
|
||||
closeChannel: OpenOrCloseChannel;
|
||||
createAddress: Scalars['String']['output'];
|
||||
createBaseInvoice: BaseInvoice;
|
||||
@ -627,6 +634,10 @@ export type MutationClaimBoltzTransactionArgs = {
|
||||
transaction: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type MutationClaimGhostAddressArgs = {
|
||||
address?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type MutationCloseChannelArgs = {
|
||||
forceClose?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
id: Scalars['String']['input'];
|
||||
@ -1199,6 +1210,11 @@ export type UserBackupInfo = {
|
||||
total_size_saved: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type UserGhostInfo = {
|
||||
__typename?: 'UserGhostInfo';
|
||||
username?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type Utxo = {
|
||||
__typename?: 'Utxo';
|
||||
address: Scalars['String']['output'];
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import { backgroundColor, textColor } from './Themes';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { Noto_Sans } from 'next/font/google';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
const notoSans = Noto_Sans({
|
||||
weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
subsets: ['latin'],
|
||||
});
|
||||
|
||||
export const GlobalStyles = createGlobalStyle`
|
||||
html, body {
|
||||
@ -11,7 +14,7 @@ export const GlobalStyles = createGlobalStyle`
|
||||
}
|
||||
* {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: ${inter.style.fontFamily}, sans-serif;
|
||||
font-family: ${notoSans.style.fontFamily}, sans-serif;
|
||||
}
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
@ -20,7 +23,7 @@ export const GlobalStyles = createGlobalStyle`
|
||||
background: ${backgroundColor};
|
||||
color: ${textColor};
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: ${inter.style.fontFamily}, sans-serif;
|
||||
font-family: ${notoSans.style.fontFamily}, sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
137
src/client/src/views/amboss/Ghost.tsx
Normal file
137
src/client/src/views/amboss/Ghost.tsx
Normal file
@ -0,0 +1,137 @@
|
||||
import styled from 'styled-components';
|
||||
import { ColorButton } from '../../components/buttons/colorButton/ColorButton';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
Separation,
|
||||
SingleLine,
|
||||
SubTitle,
|
||||
} from '../../components/generic/Styled';
|
||||
import { Link } from '../../components/link/Link';
|
||||
import { Text } from '../../components/typography/Styled';
|
||||
import { useAmbossUser } from '../../hooks/UseAmbossUser';
|
||||
import { AmbossLoginButton } from './LoginButton';
|
||||
import { mediaWidths } from '../../styles/Themes';
|
||||
import { useClaimGhostAddressMutation } from '../../graphql/mutations/__generated__/claimGhostAddress.generated';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { renderLine } from '../../components/generic/helpers';
|
||||
import { useState } from 'react';
|
||||
import { Input } from '../../components/input';
|
||||
|
||||
const S = {
|
||||
row: styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
const Buttons = () => {
|
||||
const { user } = useAmbossUser();
|
||||
|
||||
const isSubscribed = !!user?.subscription.subscribed;
|
||||
const hasClaimedAddress = !!user?.ghost.username;
|
||||
|
||||
const [savedUsername, setUsername] = useState(user?.ghost.username || '');
|
||||
|
||||
const [claimAddress, { loading }] = useClaimGhostAddressMutation({
|
||||
onCompleted: () => toast.success('Address claimed'),
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
refetchQueries: ['GetAmbossUser'],
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<SingleLine>
|
||||
<Text style={{ margin: '0' }}>Login to claim an address</Text>
|
||||
<AmbossLoginButton />
|
||||
</SingleLine>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isSubscribed) {
|
||||
if (!!hasClaimedAddress) {
|
||||
return (
|
||||
<ColorButton fullWidth loading={loading}>
|
||||
<Link href="https://amboss.space/pricing" newTab>
|
||||
Subscribe to claim a custom address
|
||||
</Link>
|
||||
</ColorButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<S.row>
|
||||
<ColorButton fullWidth loading={loading} onClick={() => claimAddress()}>
|
||||
Claim free random address
|
||||
</ColorButton>
|
||||
<ColorButton fullWidth loading={loading}>
|
||||
<Link href="https://amboss.space/pricing" newTab>
|
||||
Subscribe to claim a custom address
|
||||
</Link>
|
||||
</ColorButton>
|
||||
</S.row>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SingleLine>
|
||||
<Input
|
||||
value={savedUsername}
|
||||
placeholder={`Custom alias`}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
onEnter={() => claimAddress({ variables: { address: savedUsername } })}
|
||||
/>
|
||||
<ColorButton
|
||||
loading={loading}
|
||||
disabled={loading || savedUsername === '' || !savedUsername}
|
||||
withMargin={'0 0 0 8px'}
|
||||
onClick={() => claimAddress({ variables: { address: savedUsername } })}
|
||||
>
|
||||
Claim
|
||||
</ColorButton>
|
||||
</SingleLine>
|
||||
);
|
||||
};
|
||||
|
||||
const CurrentAddress = () => {
|
||||
const { user } = useAmbossUser();
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separation />
|
||||
{renderLine(
|
||||
'Your Ghost address',
|
||||
user.ghost.username
|
||||
? `${user.ghost.username}@ghst.to`
|
||||
: `Has not been claimed!`
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Ghost = () => {
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>Ghost Address</SubTitle>
|
||||
<Card>
|
||||
<Text>
|
||||
With a Ghost address you can have your very own
|
||||
<Link href="https://lightningaddress.com/" newTab>
|
||||
{' Lightning Address '}
|
||||
</Link>
|
||||
for you to receive payments directly to your node.
|
||||
</Text>
|
||||
<CurrentAddress />
|
||||
<Separation />
|
||||
<Buttons />
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
@ -22,6 +22,7 @@ import { LnUrlCard } from './lnurl';
|
||||
import { LnMarketsCard } from './lnmarkets';
|
||||
import { AmbossCard } from './amboss/AmbossCard';
|
||||
import { LightningAddressCard } from './lightningAddress/LightningAddress';
|
||||
import { GhostCard } from './ghost/GhostQuickAction';
|
||||
|
||||
export const QuickCard = styled.div`
|
||||
background: ${cardColor};
|
||||
@ -34,9 +35,7 @@ export const QuickCard = styled.div`
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
color: #69c0ff;
|
||||
|
||||
@ -61,6 +60,8 @@ export const QuickTitle = styled.div`
|
||||
const QuickRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin: 16px 0 32px;
|
||||
`;
|
||||
|
||||
export const QuickActions = () => {
|
||||
@ -104,6 +105,7 @@ export const QuickActions = () => {
|
||||
default:
|
||||
return (
|
||||
<QuickRow>
|
||||
<GhostCard />
|
||||
<SupportCard callback={() => setOpenCard('support')} />
|
||||
<AmbossCard />
|
||||
<QuickCard onClick={() => setOpenCard('lightning_address')}>
|
||||
|
@ -30,9 +30,7 @@ const QuickCard = styled.button`
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
color: #69c0ff;
|
||||
|
||||
|
@ -26,9 +26,7 @@ const QuickCard = styled.div`
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
color: #69c0ff;
|
||||
|
||||
|
@ -20,6 +20,7 @@ import { useCreateThunderPointsMutation } from '../../../../graphql/mutations/__
|
||||
import { toast } from 'react-toastify';
|
||||
import { useBaseConnect } from '../../../../hooks/UseBaseConnect';
|
||||
import { Pay } from '../../account/pay/Pay';
|
||||
import { getErrorContent } from '../../../../utils/error';
|
||||
|
||||
const StyledText = styled.div`
|
||||
text-align: center;
|
||||
@ -45,7 +46,9 @@ export const SupportBar = () => {
|
||||
|
||||
const [withPoints, setWithPoints] = React.useState<boolean>(false);
|
||||
|
||||
const [getInvoice, { data, loading }] = useCreateBaseInvoiceMutation();
|
||||
const [getInvoice, { data, loading }] = useCreateBaseInvoiceMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const [createPoints, { data: pointsData, called, loading: pointsLoading }] =
|
||||
useCreateThunderPointsMutation({ refetchQueries: ['GetBasePoints'] });
|
||||
|
@ -0,0 +1,57 @@
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
cardBorderColor,
|
||||
cardColor,
|
||||
mediaWidths,
|
||||
unSelectedNavButton,
|
||||
} from '../../../../styles/Themes';
|
||||
import { GhostLogo } from '../../../../components/logo/GhostIcon';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const QuickTitle = styled.div`
|
||||
font-size: 12px;
|
||||
color: ${unSelectedNavButton};
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const QuickCard = styled.div`
|
||||
background: ${cardColor};
|
||||
box-shadow: 0 8px 16px -8px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
border: 1px solid ${cardBorderColor};
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
color: #69c0ff;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
padding: 4px;
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: black;
|
||||
color: white;
|
||||
|
||||
& ${QuickTitle} {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GhostCard = () => {
|
||||
const { push } = useRouter();
|
||||
|
||||
return (
|
||||
<QuickCard onClick={() => push('/amboss')}>
|
||||
<GhostLogo size={24} />
|
||||
<QuickTitle>Ghost</QuickTitle>
|
||||
</QuickCard>
|
||||
);
|
||||
};
|
@ -15,6 +15,9 @@ export const getUserQuery = gql`
|
||||
remaining_size
|
||||
total_size_saved
|
||||
}
|
||||
ghost {
|
||||
username
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -138,3 +141,11 @@ export const getGhostPayment = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const claimGhostAddress = gql`
|
||||
mutation claimGhostAddress($address: String) {
|
||||
claimGhostAddress(address: $address) {
|
||||
username
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -8,6 +8,7 @@ import { Logger } from 'winston';
|
||||
import cookie from 'cookie';
|
||||
import {
|
||||
AmbossUser,
|
||||
ClaimGhostAddress,
|
||||
LightningAddress,
|
||||
LightningNodeSocialInfo,
|
||||
} from './amboss.types';
|
||||
@ -17,6 +18,7 @@ import { NodeService } from '../../node/node.service';
|
||||
import { UserId } from '../../security/security.types';
|
||||
import { CurrentUser } from '../../security/security.decorators';
|
||||
import {
|
||||
claimGhostAddress,
|
||||
getLightningAddresses,
|
||||
getLoginTokenQuery,
|
||||
getNodeSocialInfo,
|
||||
@ -25,6 +27,7 @@ import {
|
||||
loginMutation,
|
||||
} from './amboss.gql';
|
||||
import { AmbossService } from './amboss.service';
|
||||
import { GraphQLError } from 'graphql';
|
||||
|
||||
const ONE_MONTH_SECONDS = 60 * 60 * 24 * 30;
|
||||
|
||||
@ -198,4 +201,29 @@ export class AmbossResolver {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Mutation(() => ClaimGhostAddress)
|
||||
async claimGhostAddress(
|
||||
@Args('address', { nullable: true }) address: string | null,
|
||||
@Context() { ambossAuth }: ContextType
|
||||
) {
|
||||
if (!ambossAuth) {
|
||||
throw new GraphQLError(
|
||||
'You need to login to Amboss before you can claim your Ghost address.'
|
||||
);
|
||||
}
|
||||
|
||||
const { data, error } = await this.fetchService.graphqlFetchWithProxy(
|
||||
this.configService.get('urls.amboss'),
|
||||
claimGhostAddress,
|
||||
{ address },
|
||||
{ authorization: `Bearer ${ambossAuth}` }
|
||||
);
|
||||
|
||||
if (!data?.claimGhostAddress || error) {
|
||||
throw new GraphQLError('Error claiming Ghost address.');
|
||||
}
|
||||
|
||||
return data.claimGhostAddress;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,12 @@ export class UserBackupInfo {
|
||||
remaining_size: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class UserGhostInfo {
|
||||
@Field({ nullable: true })
|
||||
username: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class AmbossUser {
|
||||
@Field(() => AmbossSubscription)
|
||||
@ -35,6 +41,9 @@ export class AmbossUser {
|
||||
|
||||
@Field(() => UserBackupInfo)
|
||||
backups: UserBackupInfo;
|
||||
|
||||
@Field(() => UserGhostInfo)
|
||||
ghost: UserGhostInfo;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
@ -73,6 +82,12 @@ export class LightningNodeSocialInfo {
|
||||
socials: NodeSocial;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ClaimGhostAddress {
|
||||
@Field()
|
||||
username: string;
|
||||
}
|
||||
|
||||
export type NodeAlias = {
|
||||
alias: string;
|
||||
pub_key: string;
|
||||
|
@ -3,6 +3,10 @@ import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
||||
import { gql } from 'graphql-tag';
|
||||
import { FetchService } from '../../fetch/fetch.service';
|
||||
import { BaseInvoice, BaseNode, BasePoints } from './base.types';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { Logger } from 'winston';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
|
||||
const getBaseCanConnectQuery = gql`
|
||||
{
|
||||
@ -54,7 +58,8 @@ const createThunderPointsQuery = gql`
|
||||
export class BaseResolver {
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private fetchService: FetchService
|
||||
private fetchService: FetchService,
|
||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
|
||||
) {}
|
||||
|
||||
@Query(() => Boolean)
|
||||
@ -97,7 +102,9 @@ export class BaseResolver {
|
||||
|
||||
@Mutation(() => BaseInvoice)
|
||||
async createBaseInvoice(@Args('amount') amount: number) {
|
||||
if (!amount) return '';
|
||||
if (!amount) {
|
||||
throw new GraphQLError('No amount provided for donation invoice.');
|
||||
}
|
||||
|
||||
const { data, error } = await this.fetchService.graphqlFetchWithProxy(
|
||||
this.configService.get('urls.tbase'),
|
||||
@ -105,10 +112,12 @@ export class BaseResolver {
|
||||
{ amount }
|
||||
);
|
||||
|
||||
if (error) return null;
|
||||
if (data?.createInvoice) return data.createInvoice;
|
||||
if (error || !data?.createInvoice) {
|
||||
this.logger.error('Error getting donation invoice.', { error, data });
|
||||
throw new GraphQLError('Error creating donation invoice.');
|
||||
}
|
||||
|
||||
return null;
|
||||
return data.createInvoice;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
|
Loading…
Reference in New Issue
Block a user