Skip to content

Commit

Permalink
refactor: allow pasting Drip List URLs when setting streams (#1097)
Browse files Browse the repository at this point in the history
Co-authored-by: Jason Efstathiou <[email protected]>
  • Loading branch information
jtourkos and efstajas authored Jun 3, 2024
1 parent 9f16d67 commit 30ea43b
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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',
};
Expand All @@ -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',
};
Expand All @@ -58,41 +92,41 @@
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);
</script>

<TextInput
autocomplete
autocapitalize={false}
autocorrect={false}
showSuccessCheck
validationState={addressValidationState}
validationState={inputValidationState}
bind:value
placeholder="Ethereum address or ENS name"
placeholder="Ethereum address, ENS name, or Drip List URL"
/>
29 changes: 17 additions & 12 deletions src/lib/flows/create-stream-flow/input-details.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<StepComponentEvents>();
Expand Down Expand Up @@ -246,26 +247,30 @@
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}
/>
<StepHeader
headline={receiver ? 'Start a Continuous Donation' : 'Create stream'}
description="Stream any ERC-20 token from your Drips account."
/>
{#if !nameInputHidden}
<FormField title="Stream name*">
<TextInput bind:value={streamNameValue} placeholder="Enter any name" />
</FormField>
{/if}
<FormField title="Stream name*">
<TextInput bind:value={streamNameValue} placeholder="Enter any name" />
</FormField>
{#if !receiver}
<FormField title="Stream to*">
<InputAddress
<InputStreamReceiver
bind:value={recipientInputValue}
on:validationChange={onRecipientInputValidationChange}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/routes/app/(app)/funds/sections/streams.section.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 30ea43b

Please sign in to comment.