Page MenuHomePhabricator

D12566.id41912.diff
No OneTemporary

D12566.id41912.diff

diff --git a/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css
new file mode 100644
--- /dev/null
+++ b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.css
@@ -0,0 +1,21 @@
+.inputLabel {
+ color: var(--text-background-primary-default);
+ margin-bottom: 8px;
+}
+
+.dropdownInputContainer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.errorMessage {
+ text-align: center;
+ color: var(--text-background-danger-default);
+ font-size: var(--s-font-14);
+ visibility: hidden;
+}
+
+.errorMessageVisible {
+ visibility: visible;
+}
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
new file mode 100644
--- /dev/null
+++ b/web/tag-farcaster-channel/create-farcaster-channel-tag-modal.react.js
@@ -0,0 +1,135 @@
+// @flow
+
+import classNames from 'classnames';
+import invariant from 'invariant';
+import * as React from 'react';
+
+import { useModalContext } from 'lib/components/modal-provider.react.js';
+import { NeynarClientContext } from 'lib/components/neynar-client-provider.react.js';
+import {
+ tagFarcasterChannelErrorMessages,
+ useCreateFarcasterChannelTag,
+} from 'lib/shared/community-utils.js';
+import { useCurrentUserFID } from 'lib/utils/farcaster-utils.js';
+
+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 Modal from '../modals/modal.react.js';
+
+type Props = {
+ +communityID: string,
+};
+
+function CreateFarcasterChannelTagModal(props: Props): React.Node {
+ const { communityID } = props;
+
+ const { popModal } = useModalContext();
+
+ const fid = useCurrentUserFID();
+ invariant(fid, 'FID should be set');
+
+ const neynarClientContext = React.useContext(NeynarClientContext);
+ invariant(neynarClientContext, 'NeynarClientContext is missing');
+
+ const { client } = neynarClientContext;
+
+ const [channelOptions, setChannelOptions] = React.useState<
+ $ReadOnlyArray<DropdownOption>,
+ >([]);
+ const [selectedOption, setSelectedOption] = React.useState<?string>(null);
+ const [error, setError] = React.useState<?string>(null);
+
+ React.useEffect(() => {
+ void (async () => {
+ const channels = await client.fetchFollowedFarcasterChannels(fid);
+
+ const sortedChannels = channels
+ .sort((a, b) => a.id.localeCompare(b.id))
+ .map(channel => ({
+ id: channel.id,
+ name: `/${channel.id}`,
+ }));
+
+ setChannelOptions(sortedChannels);
+ })();
+ }, [client, fid]);
+
+ const onChangeSelectedOption = React.useCallback((option: string) => {
+ setError(null);
+ setSelectedOption(option);
+ }, []);
+
+ const { createTag, isLoading } = useCreateFarcasterChannelTag(
+ communityID,
+ setError,
+ popModal,
+ );
+
+ const onClickTagChannel = React.useCallback(() => {
+ if (!selectedOption) {
+ return;
+ }
+
+ createTag(selectedOption);
+ }, [createTag, selectedOption]);
+
+ const buttonDisabled = isLoading || !selectedOption;
+
+ const primaryButton = React.useMemo(() => {
+ return (
+ <Button
+ variant="filled"
+ buttonColor={buttonThemes.standard}
+ onClick={onClickTagChannel}
+ disabled={buttonDisabled}
+ >
+ Create tag
+ </Button>
+ );
+ }, [onClickTagChannel, buttonDisabled]);
+
+ const errorMessageClassName = classNames(css.errorMessage, {
+ [css.errorMessageVisible]: error,
+ });
+
+ const errorMessage =
+ error && tagFarcasterChannelErrorMessages[error]
+ ? tagFarcasterChannelErrorMessages[error]
+ : 'Unknown error.';
+
+ const createFarcasterChannelTagModal = React.useMemo(
+ () => (
+ <Modal
+ name="Create a tag"
+ onClose={popModal}
+ size="large"
+ primaryButton={primaryButton}
+ >
+ <div className={css.inputLabel}>Farcaster channel</div>
+ <div className={css.dropdownInputContainer}>
+ <Dropdown
+ options={channelOptions}
+ defaultLabel="Select a channel"
+ activeSelection={selectedOption}
+ setActiveSelection={onChangeSelectedOption}
+ />
+ </div>
+ <div className={errorMessageClassName}>{errorMessage}</div>
+ </Modal>
+ ),
+ [
+ channelOptions,
+ errorMessage,
+ errorMessageClassName,
+ onChangeSelectedOption,
+ popModal,
+ primaryButton,
+ selectedOption,
+ ],
+ );
+
+ return createFarcasterChannelTagModal;
+}
+
+export default CreateFarcasterChannelTagModal;
diff --git a/web/tag-farcaster-channel/tag-farcaster-channel-modal.react.js b/web/tag-farcaster-channel/tag-farcaster-channel-modal.react.js
--- a/web/tag-farcaster-channel/tag-farcaster-channel-modal.react.js
+++ b/web/tag-farcaster-channel/tag-farcaster-channel-modal.react.js
@@ -10,8 +10,10 @@
} from 'lib/shared/community-utils.js';
import type { CommunityInfo } from 'lib/types/community-types.js';
+import CreateFarcasterChannelTagModal from './create-farcaster-channel-tag-modal.react.js';
import RemoveTagButton from './remove-tag-button.react.js';
import css from './tag-farcaster-channel-modal.css';
+import Button, { buttonThemes } from '../components/button.react.js';
import Modal from '../modals/modal.react.js';
import { useSelector } from '../redux/redux-utils.js';
@@ -22,7 +24,7 @@
function TagFarcasterChannelModal(props: Props): React.Node {
const { communityID } = props;
- const { popModal } = useModalContext();
+ const { popModal, pushModal } = useModalContext();
const communityInfo: ?CommunityInfo = useSelector(
state => state.communityStore.communityInfos[communityID],
@@ -30,6 +32,12 @@
const [removeTagError, setRemoveTagError] = React.useState<?string>();
+ const openCreateFarcasterChannelTagModal = React.useCallback(
+ () =>
+ pushModal(<CreateFarcasterChannelTagModal communityID={communityID} />),
+ [communityID, pushModal],
+ );
+
const channelNameTextContent = React.useMemo(() => {
if (!communityInfo?.farcasterChannelID) {
return (
@@ -56,9 +64,21 @@
/>
);
}
- // TODO: Implement TagChannelButton
- return null;
- }, [communityID, communityInfo?.farcasterChannelID]);
+
+ return (
+ <Button
+ variant="filled"
+ buttonColor={buttonThemes.standard}
+ onClick={openCreateFarcasterChannelTagModal}
+ >
+ Tag channel
+ </Button>
+ );
+ }, [
+ communityID,
+ communityInfo?.farcasterChannelID,
+ openCreateFarcasterChannelTagModal,
+ ]);
const errorMessageClassName = classNames(css.errorMessage, {
[css.errorMessageVisible]: removeTagError,

File Metadata

Mime Type
text/plain
Expires
Sun, Oct 6, 2:02 PM (20 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2249847
Default Alt Text
D12566.id41912.diff (6 KB)

Event Timeline