diff --git a/web/components/action-buttons/FollowButton.tsx b/web/components/action-buttons/FollowButton.tsx index 710a584c5..ae9f5a787 100644 --- a/web/components/action-buttons/FollowButton.tsx +++ b/web/components/action-buttons/FollowButton.tsx @@ -1,11 +1,17 @@ import { Button } from 'antd'; import { HeartFilled } from '@ant-design/icons'; import { useState } from 'react'; +import { useRecoilValue } from 'recoil'; import Modal from '../ui/Modal/Modal'; +import FollowModal from '../modals/Follow/FollowModal'; import s from './ActionButton.module.scss'; +import { clientConfigStateAtom } from '../stores/ClientConfigStore'; +import { ClientConfig } from '../../interfaces/client-config.model'; export default function FollowButton() { const [showModal, setShowModal] = useState(false); + const clientConfig = useRecoilValue(clientConfigStateAtom); + const { name } = clientConfig; const buttonClicked = () => { setShowModal(true); @@ -21,11 +27,9 @@ export default function FollowButton() { > Follow - setShowModal(false)} - /> + setShowModal(false)}> + setShowModal(false)} /> + ); } diff --git a/web/components/modals/Follow/FollowModal.tsx b/web/components/modals/Follow/FollowModal.tsx new file mode 100644 index 000000000..a35ac5a6b --- /dev/null +++ b/web/components/modals/Follow/FollowModal.tsx @@ -0,0 +1,90 @@ +import { Input, Button, Alert, Spin } from 'antd'; +import { useState } from 'react'; + +const ENDPOINT = '/api/remotefollow'; + +interface Props { + handleClose: () => void; +} + +function validateAccount(a) { + const sanitized = a.replace(/^@+/, ''); + const regex = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return regex.test(String(sanitized).toLowerCase()); +} + +export default function FollowModal(props: Props) { + const { handleClose } = props; + const [account, setAccount] = useState(null); + const [valid, setValid] = useState(false); + const [loading, setLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + + const handleAccountChange = a => { + setAccount(a); + if (validateAccount(a)) { + setValid(true); + } else { + setValid(false); + } + }; + + const remoteFollowButtonPressed = async () => { + if (!valid) { + return; + } + + setLoading(true); + + try { + const sanitizedAccount = account.replace(/^@+/, ''); + const request = { account: sanitizedAccount }; + const rawResponse = await fetch(ENDPOINT, { + method: 'POST', + body: JSON.stringify(request), + }); + const result = await rawResponse.json(); + + if (result.redirectUrl) { + window.open(result.redirectUrl, '_blank'); + handleClose(); + } + if (!result.success) { + setErrorMessage(result.message); + setLoading(false); + return; + } + if (!result.redirectUrl) { + setErrorMessage('Unable to follow.'); + setLoading(false); + return; + } + } catch (e) { + setErrorMessage(e.message); + } + setLoading(false); + }; + + return ( + + {errorMessage && ( + + )} + handleAccountChange(e.target.value)} + placeholder="Your fediverse account @account@server" + defaultValue={account} + /> + +
+ Information about following a Fediverse account and next steps how to create a Fediverse + account goes here. +
+
+ ); +} diff --git a/web/components/modals/FollowModal.tsx b/web/components/modals/FollowModal.tsx deleted file mode 100644 index 1849cb455..000000000 --- a/web/components/modals/FollowModal.tsx +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -interface Props {} - -export default function FollowModal(props: Props) { - return
Component goes here
; -} diff --git a/web/stories/FollowModal.stories.tsx b/web/stories/FollowModal.stories.tsx index a959b2207..47050d4f1 100644 --- a/web/stories/FollowModal.stories.tsx +++ b/web/stories/FollowModal.stories.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { ComponentStory, ComponentMeta } from '@storybook/react'; -import FollowModal from '../components/modals/FollowModal'; +import FollowModal from '../components/modals/Follow/FollowModal'; import FollowModalMock from './assets/mocks/follow-modal.png'; const Example = () => (
- +
);