import { t } from 'i18next' import { useCallback, useEffect, useMemo, useState } from 'react' import { useProjectWalletAPI } from '@/modules/project/API/useProjectWalletAPI.ts' import { useProjectAtom, useWalletAtom } from '@/modules/project/hooks/useProjectAtom' import { LightingWalletForm, Limits, LNAddressEvaluationState } from '@/shared/types/wallet' import { useDebounce } from '../../../../../shared/hooks' import { CreateWalletInput, LightningAddressContributionLimits, useLightningAddressVerifyLazyQuery, WalletOffChainContributionLimits, WalletOnChainContributionLimits, WalletResourceType, } from '../../../../../types' import { toInt, useNotification, validateEmail } from '../../../../../utils' interface useWalletFormProps { onSubmit: (createWalletInput: CreateWalletInput | null) => void isEdit?: boolean } export { LNAddressEvaluationState } export type { LightingWalletForm, Limits } export type WalletForm = { handleConfirm: () => void lightningAddress: LightingWalletForm isFormDirty: () => boolean createWalletInput: CreateWalletInput | null isLightningAddressInValid: boolean limits: Limits } const DEFAULT_LIGHTNING_FEE_PERCENTAGE = 0.05 export const useWalletForm = ({ onSubmit, isEdit }: useWalletFormProps): WalletForm => { const { toast } = useNotification() const { project } = useProjectAtom() const { queryProjectWalletConnectionDetails } = useProjectWalletAPI(true) const { execute: queryProjectWalletConnectionDetailsExecute } = queryProjectWalletConnectionDetails const { wallet, walletConnectionDetails } = useWalletAtom() const projectWallet = useMemo(() => ({ ...wallet, ...walletConnectionDetails }), [wallet, walletConnectionDetails]) const [lightningAddressFormValue, setLightningAddressFormValue] = useState('') const [lightningAddressFormError, setLightningAddressFormError] = useState(null) const [lnAddressEvaluationState, setLnAddressEvaluationState] = useState( LNAddressEvaluationState.IDLE, ) useEffect(() => { queryProjectWalletConnectionDetailsExecute() }, [queryProjectWalletConnectionDetailsExecute]) const [limits, setLimits] = useState< LightningAddressContributionLimits | WalletOffChainContributionLimits | WalletOnChainContributionLimits >(projectWallet?.limits?.contribution?.offChain || {}) const debouncedLightningAddress = useDebounce(lightningAddressFormValue, 200) const [evaluateLightningAddress, { loading: isEvaluatingLightningAddress }] = useLightningAddressVerifyLazyQuery({ onCompleted({ lightningAddressVerify: { valid, reason, limits } }) { if (Boolean(valid) === false) { setLnAddressEvaluationState(LNAddressEvaluationState.FAILED) setLightningAddressFormError( t('We could not validate this as a working Lightning Address: {{reason}}', { reason: reason || '' }), ) } else { setLightningAddressFormError('') setLnAddressEvaluationState(LNAddressEvaluationState.SUCCEEDED) if (limits) { setLimits(limits) } } }, }) const validateLightningAddressFormat = useCallback((lightningAddress: string) => { if (!lightningAddress) { setLightningAddressFormError(null) setLnAddressEvaluationState(LNAddressEvaluationState.IDLE) return false } if (lightningAddress.toLowerCase().endsWith('@geyser.fund')) { setLightningAddressFormError( t('Custom Lightning Addresses cannot end with {{domain}}', { domain: '@geyser.fund' }), ) setLnAddressEvaluationState(LNAddressEvaluationState.FAILED) return false } if (validateEmail(lightningAddress) === false) { setLightningAddressFormError(t('Please use a valid email-formatted address for your Lightning Address')) setLnAddressEvaluationState(LNAddressEvaluationState.FAILED) return false } setLightningAddressFormError(null) return true }, []) const validateLightningAddress = useCallback(async () => { if (!lightningAddressFormValue) { setLightningAddressFormError(null) setLnAddressEvaluationState(LNAddressEvaluationState.IDLE) return } await evaluateLightningAddress({ variables: { lightningAddress: lightningAddressFormValue }, }) }, [evaluateLightningAddress, lightningAddressFormValue]) useEffect(() => { const valid = validateLightningAddressFormat(debouncedLightningAddress) if (valid) { validateLightningAddress() } }, [debouncedLightningAddress, validateLightningAddress, validateLightningAddressFormat]) useEffect(() => { if (projectWallet?.connectionDetails) { setLightningAddressFormValue(projectWallet.connectionDetails.lightningAddress) } }, [projectWallet]) const createWalletInput: CreateWalletInput | null = useMemo(() => { const resourceInput: { resourceId: number resourceType: WalletResourceType } = { resourceId: toInt(project?.id), resourceType: WalletResourceType.Project, } if (!lightningAddressFormValue) { return null } return { lightningAddressConnectionDetailsInput: { lightningAddress: lightningAddressFormValue, }, resourceInput, feePercentage: DEFAULT_LIGHTNING_FEE_PERCENTAGE, } }, [project, lightningAddressFormValue]) const handleConfirm = useCallback(async () => { if (lightningAddressFormValue) { if (!validateLightningAddressFormat(lightningAddressFormValue)) { return } if (lnAddressEvaluationState !== LNAddressEvaluationState.SUCCEEDED) { const response = await evaluateLightningAddress({ variables: { lightningAddress: lightningAddressFormValue }, }) if (!response?.data?.lightningAddressVerify?.valid) { return } } } const walletInputToSubmit = createWalletInput if (isEdit && !walletInputToSubmit) { toast({ title: t('failed to create project wallet'), description: t('please provide valid wallet details'), status: 'error', }) return } onSubmit(walletInputToSubmit) }, [ createWalletInput, onSubmit, toast, lightningAddressFormValue, lnAddressEvaluationState, evaluateLightningAddress, validateLightningAddressFormat, isEdit, ]) const isFormDirty = useCallback(() => { if (!projectWallet?.connectionDetails) { return true } return ( Boolean(projectWallet.connectionDetails.lightningAddress) && projectWallet.connectionDetails.lightningAddress !== lightningAddressFormValue ) }, [projectWallet, lightningAddressFormValue]) const isLightningAddressInValid = useMemo(() => { if ( (lnAddressEvaluationState !== LNAddressEvaluationState.SUCCEEDED && lnAddressEvaluationState !== LNAddressEvaluationState.IDLE) || Boolean(lightningAddressFormError) ) { return true } return false }, [lnAddressEvaluationState, lightningAddressFormError]) return { handleConfirm, lightningAddress: { error: lightningAddressFormError, state: lnAddressEvaluationState, value: lightningAddressFormValue, setValue: setLightningAddressFormValue, evaluating: isEvaluatingLightningAddress, validate: validateLightningAddress, }, limits, isFormDirty, createWalletInput, isLightningAddressInValid, } }