Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32212292
D12566.1765140713.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D12566.1765140713.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 7, 8:51 PM (21 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5846002
Default Alt Text
D12566.1765140713.diff (6 KB)
Attached To
Mode
D12566: [web] introduce CreateFarcasterChannelTagModal
Attached
Detach File
Event Timeline
Log In to Comment