diff --git a/src/lib/components/input-address/input-address.svelte b/src/lib/components/input-address/input-stream-receiver.svelte similarity index 50% rename from src/lib/components/input-address/input-address.svelte rename to src/lib/components/input-address/input-stream-receiver.svelte index 4200123af..251c14cad 100644 --- a/src/lib/components/input-address/input-address.svelte +++ b/src/lib/components/input-address/input-stream-receiver.svelte @@ -4,6 +4,9 @@ import { ethers } from 'ethers'; import type { TextInputValidationState } from '$lib/components/text-input/text-input'; import { createEventDispatcher } from 'svelte'; + import { BASE_URL } from '$lib/utils/base-url'; + import assert from '$lib/utils/assert'; + import { Utils } from 'radicle-drips'; export let value: string | undefined = undefined; export let validatedValue: string | undefined = undefined; @@ -17,18 +20,49 @@ const dispatch = createEventDispatcher(); - let addressValidationState: TextInputValidationState = { type: 'unvalidated' }; + let inputValidationState: TextInputValidationState = { type: 'unvalidated' }; - async function validateAddress(input: string | undefined) { + async function validateInput(input: string | undefined) { if (!input) { - addressValidationState = { type: 'unvalidated' }; + inputValidationState = { type: 'unvalidated' }; validatedValue = undefined; return; } - if (input.endsWith('.eth')) { + if (input.includes(`${BASE_URL}/app/drip-lists/`)) { + inputValidationState = { + type: 'pending', + }; + + const dripListId = input.substring(input.lastIndexOf('/') + 1); + assert(dripListId); + + if (Utils.AccountId.getDriver(dripListId) !== 'nft') { + inputValidationState = { + type: 'invalid', + message: 'Invalid Drip List URL', + }; + + throw new Error('Invalid Drip List URL'); + } + + if (dripListId) { + validatedValue = dripListId; + value = dripListId; + + inputValidationState = { + type: 'valid', + }; + } else { + validatedValue = undefined; + inputValidationState = { + type: 'invalid', + message: 'Unable to resolve Drip List URL', + }; + } + } else if (input.endsWith('.eth')) { // lookup ENS - addressValidationState = { + inputValidationState = { type: 'pending', }; @@ -38,12 +72,12 @@ validatedValue = address; value = address; - addressValidationState = { + inputValidationState = { type: 'valid', }; } else { validatedValue = undefined; - addressValidationState = { + inputValidationState = { type: 'invalid', message: 'Unable to resolve ENS name', }; @@ -58,33 +92,33 @@ if (exclusionMatch) { // is excluded! - addressValidationState = { + inputValidationState = { type: 'invalid', message: exclusionMatch.msg, }; } else { // valid - addressValidationState = { + inputValidationState = { type: 'valid', }; } } else { // invalid validatedValue = undefined; - addressValidationState = { + inputValidationState = { type: 'invalid', - message: 'Enter a valid Ethereum address or ENS name.', + message: 'Enter a valid Ethereum address, ENS name, or Drip List URL.', }; } } - // ensure initial value is validated since validateAddress() is async + // ensure initial value is validated since validateInput() is async if (value?.length) { - validateAddress(value).then(() => dispatch('validationChange', addressValidationState)); + validateInput(value).then(() => dispatch('validationChange', inputValidationState)); } - $: validateAddress(value); - $: dispatch('validationChange', addressValidationState); + $: validateInput(value); + $: dispatch('validationChange', inputValidationState); diff --git a/src/lib/flows/create-stream-flow/input-details.svelte b/src/lib/flows/create-stream-flow/input-details.svelte index ee048c433..00b9ffdce 100644 --- a/src/lib/flows/create-stream-flow/input-details.svelte +++ b/src/lib/flows/create-stream-flow/input-details.svelte @@ -40,7 +40,6 @@ import Token from '$lib/components/token/token.svelte'; import type { Items } from '$lib/components/list-select/list-select.types'; import formatTokenAmount from '$lib/utils/format-token-amount'; - import InputAddress from '$lib/components/input-address/input-address.svelte'; import Toggleable from '$lib/components/toggleable/toggleable.svelte'; import createStream from './methods/create-stream'; import { get, type Writable } from 'svelte/store'; @@ -59,6 +58,8 @@ CreateStreamFlowAddressDriverAccountFragment, CreateStreamFlowDetailsNftDriverAccountFragment, } from './__generated__/gql.generated'; + import InputStreamReceiver from '$lib/components/input-address/input-stream-receiver.svelte'; + import { isAddress } from 'ethers/lib/utils'; const dispatch = createEventDispatcher(); @@ -246,11 +247,17 @@ to={receiver ? receiver : recipientInputValidationState.type === 'valid' && recipientInputValue - ? { - __typename: 'AddressDriverAccount', - driver: Driver.Address, - address: recipientInputValue, - } + ? isAddress(recipientInputValue) // TODO: Extract to function when project receiver is supported. + ? { + __typename: 'AddressDriverAccount', + driver: Driver.Address, + address: recipientInputValue, + } + : { + __typename: 'NftDriverAccount', + driver: Driver.Nft, + accountId: recipientInputValue, + } : undefined} amountPerSecond={amountValidationState?.type === 'valid' ? amountPerSecond : undefined} /> @@ -258,14 +265,12 @@ headline={receiver ? 'Start a Continuous Donation' : 'Create stream'} description="Stream any ERC-20 token from your Drips account." /> - {#if !nameInputHidden} - - - - {/if} + + + {#if !receiver} - diff --git a/src/routes/app/(app)/[accountId]/tokens/[token]/streams/[dripId]/+page.svelte b/src/routes/app/(app)/[accountId]/tokens/[token]/streams/[dripId]/+page.svelte index 869c8a66b..c60c7dd8c 100644 --- a/src/routes/app/(app)/[accountId]/tokens/[token]/streams/[dripId]/+page.svelte +++ b/src/routes/app/(app)/[accountId]/tokens/[token]/streams/[dripId]/+page.svelte @@ -74,7 +74,8 @@ $: { if (stream) { streamName = - stream.receiver.driver === 'nft' ? 'Continuous donation' : stream.name ?? 'Unnamed stream'; + stream.name || + (stream.receiver.driver === 'nft' ? 'Continuous donation' : 'Unnamed stream'); } } diff --git a/src/routes/app/(app)/funds/sections/streams.section.svelte b/src/routes/app/(app)/funds/sections/streams.section.svelte index 0f2e0cd0e..8f8facac0 100644 --- a/src/routes/app/(app)/funds/sections/streams.section.svelte +++ b/src/routes/app/(app)/funds/sections/streams.section.svelte @@ -94,7 +94,8 @@ // TODO: Donʼt presume that any stream to an NFT subaccount is going to a Drip List. const streamName = - stream.receiver.driver === 'nft' ? 'Continuous donation' : stream.name ?? 'Unnamed stream'; + stream.name || + (stream.receiver.driver === 'nft' ? 'Continuous donation' : 'Unnamed stream'); return { streamId: stream.id,