diff --git a/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css --- a/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css +++ b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css @@ -1,6 +1,6 @@ .inputLabel { color: var(--text-background-primary-default); - margin-bottom: 8px; + margin-bottom: 16px; } .dropdownInputContainer { @@ -9,12 +9,26 @@ align-items: center; } -.errorMessageContainer { - height: 18px; +.textInputContainer { + margin: 0 32px; +} + +.tagFarcasterChannelByNameContainer { + display: flex; + flex-direction: column; + justify-content: flex-end; + margin-top: 24px; } .errorMessage { + margin-top: 4px; text-align: center; color: var(--text-background-danger-default); font-size: var(--s-font-14); } + +.bottomContainer { + height: 112px; + display: flex; + flex-direction: column; +} diff --git a/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.react.js b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.react.js --- a/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.react.js +++ b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.react.js @@ -14,6 +14,7 @@ import css from './create-farcaster-channel-tag-modal.css'; import Button, { buttonThemes } from '../components/button.react.js'; import Dropdown, { type DropdownOption } from '../components/dropdown.react.js'; +import Input from '../modals/input.react.js'; import Modal from '../modals/modal.react.js'; type Props = { @@ -37,6 +38,7 @@ $ReadOnlyArray, >([]); const [selectedOption, setSelectedOption] = React.useState(null); + const [channelNameText, setChannelSelectionText] = React.useState(''); const [error, setError] = React.useState(null); React.useEffect(() => { @@ -50,30 +52,74 @@ name: `/${channel.id}`, })); - setChannelOptions(sortedChannels); + const options = [{ id: 'other', name: 'Other' }, ...sortedChannels]; + + setChannelOptions(options); })(); }, [client, fid]); const onChangeSelectedOption = React.useCallback((option: string) => { setError(null); + setChannelSelectionText(''); setSelectedOption(option); }, []); + const onChangeChannelNameText = React.useCallback( + (event: SyntheticEvent) => { + setChannelSelectionText(event.currentTarget.value); + }, + [], + ); + + const tagFarcasterChannelByName = React.useMemo(() => { + if (selectedOption !== 'other') { + return null; + } + + return ( +
+
Channel name
+
+ +
+
+ ); + }, [channelNameText, onChangeChannelNameText, selectedOption]); + const { createTag, isLoading } = useCreateFarcasterChannelTag( communityID, setError, popModal, ); - const onClickTagChannel = React.useCallback(() => { + const onClickTagChannel = React.useCallback(async () => { if (!selectedOption) { return; + } else if (selectedOption === 'other') { + const channelInfo = + await neynarClientContext.client.fetchFarcasterChannelByName( + channelNameText, + ); + + if (!channelInfo) { + setError('channel_not_found'); + return; + } + + createTag(channelInfo.id); + } else { + createTag(selectedOption); } + }, [channelNameText, createTag, neynarClientContext.client, selectedOption]); - createTag(selectedOption); - }, [createTag, selectedOption]); - - const buttonDisabled = isLoading || !selectedOption; + const buttonDisabled = + isLoading || + !selectedOption || + (selectedOption === 'other' && !channelNameText); const primaryButton = React.useMemo(() => { return ( @@ -117,7 +163,10 @@ setActiveSelection={onChangeSelectedOption} /> -
{errorMessage}
+
+ {tagFarcasterChannelByName} + {errorMessage} +
), [ @@ -127,6 +176,7 @@ popModal, primaryButton, selectedOption, + tagFarcasterChannelByName, ], );