import React, { useState, useContext, useEffect, FC } from 'react'; import { Typography, Table, Button, Modal, Input } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { CaretDownOutlined, CaretUpOutlined, DeleteOutlined } from '@ant-design/icons'; import { SocialDropdown } from './SocialDropdown'; import { fetchData, SOCIAL_PLATFORMS_LIST } from '../../utils/apis'; import { ServerStatusContext } from '../../utils/server-status-context'; import { API_SOCIAL_HANDLES, postConfigUpdateToAPI, RESET_TIMEOUT, DEFAULT_SOCIAL_HANDLE, OTHER_SOCIAL_HANDLE_OPTION, } from '../../utils/config-constants'; import { SocialHandle, UpdateArgs } from '../../types/config-section'; import isValidUrl, { DEFAULT_TEXTFIELD_URL_PATTERN } from '../../utils/urls'; import { TextField } from './TextField'; import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses'; import { FormStatusIndicator } from './FormStatusIndicator'; const { Title } = Typography; export const EditSocialLinks: FC = () => { const [availableIconsList, setAvailableIconsList] = useState([]); const [currentSocialHandles, setCurrentSocialHandles] = useState([]); const [displayModal, setDisplayModal] = useState(false); const [displayOther, setDisplayOther] = useState(false); const [modalProcessing, setModalProcessing] = useState(false); const [editId, setEditId] = useState(-1); // current data inside modal const [modalDataState, setModalDataState] = useState(DEFAULT_SOCIAL_HANDLE); const [submitStatus, setSubmitStatus] = useState(null); const serverStatusData = useContext(ServerStatusContext); const { serverConfig, setFieldInConfigState } = serverStatusData || {}; const { instanceDetails } = serverConfig; const { socialHandles: initialSocialHandles } = instanceDetails; let resetTimer = null; const PLACEHOLDERS = { mastodon: 'https://mastodon.social/@username', twitter: 'https://twitter.com/username', }; const getAvailableIcons = async () => { try { const result = await fetchData(SOCIAL_PLATFORMS_LIST, { auth: false }); const list = Object.keys(result).map(item => ({ key: item, ...result[item], })); setAvailableIconsList(list); } catch (error) { console.log(error); // do nothing } }; const isPredefinedSocial = (platform: string) => availableIconsList.find(item => item.key === platform) || false; const selectedOther = modalDataState.platform !== '' && !availableIconsList.find(item => item.key === modalDataState.platform); useEffect(() => { getAvailableIcons(); }, []); useEffect(() => { if (instanceDetails.socialHandles) { setCurrentSocialHandles(initialSocialHandles); } }, [instanceDetails]); const resetStates = () => { setSubmitStatus(null); resetTimer = null; clearTimeout(resetTimer); }; const resetModal = () => { setDisplayModal(false); setEditId(-1); setDisplayOther(false); setModalProcessing(false); setModalDataState({ ...DEFAULT_SOCIAL_HANDLE }); }; const handleModalCancel = () => { resetModal(); }; const updateModalState = (fieldName: string, value: string) => { setModalDataState({ ...modalDataState, [fieldName]: value, }); }; const handleDropdownSelect = (value: string) => { if (value === OTHER_SOCIAL_HANDLE_OPTION) { setDisplayOther(true); updateModalState('platform', ''); } else { setDisplayOther(false); updateModalState('platform', value); } }; const handleOtherNameChange = event => { const { value } = event.target; updateModalState('platform', value); }; const handleUrlChange = ({ value }: UpdateArgs) => { updateModalState('url', value); }; // posts all the variants at once as an array obj const postUpdateToAPI = async (postValue: any) => { await postConfigUpdateToAPI({ apiPath: API_SOCIAL_HANDLES, data: { value: postValue }, onSuccess: () => { setFieldInConfigState({ fieldName: 'socialHandles', value: postValue, path: 'instanceDetails', }); // close modal setModalProcessing(false); handleModalCancel(); setSubmitStatus(createInputStatus(STATUS_SUCCESS)); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, onError: (message: string) => { setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${message}`)); setModalProcessing(false); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, }); }; // on Ok, send all of dataState to api // show loading // close modal when api is done const handleModalOk = () => { setModalProcessing(true); const postData = currentSocialHandles.length ? [...currentSocialHandles] : []; if (editId === -1) { postData.push(modalDataState); } else { postData.splice(editId, 1, modalDataState); } postUpdateToAPI(postData); }; const handleDeleteItem = (index: number) => { const postData = [...currentSocialHandles]; postData.splice(index, 1); postUpdateToAPI(postData); }; const handleMoveItemUp = (index: number) => { if (index <= 0 || index >= currentSocialHandles.length) { return; } const postData = [...currentSocialHandles]; const tmp = postData[index - 1]; postData[index - 1] = postData[index]; postData[index] = tmp; postUpdateToAPI(postData); }; const handleMoveItemDown = (index: number) => { if (index < 0 || index >= currentSocialHandles.length - 1) { return; } const postData = [...currentSocialHandles]; const tmp = postData[index + 1]; postData[index + 1] = postData[index]; postData[index] = tmp; postUpdateToAPI(postData); }; const socialHandlesColumns: ColumnsType = [ { title: 'Social Link', dataIndex: '', key: 'combo', render: (data, record) => { const { platform, url } = record; const platformInfo = isPredefinedSocial(platform); // custom platform case if (!platformInfo) { return (

{platform} {url}

); } const { icon, platform: platformName } = platformInfo; return (

{platformName} {url}

); }, }, { title: '', dataIndex: '', key: 'edit', render: (data, record, index) => (
), }, ]; const okButtonProps = { disabled: !isValidUrl(modalDataState.url), }; const otherField = (
); return (
Your Social Handles

Add all your social media handles and links to your other profiles here.

`${record.platform}-${record.url}`} columns={socialHandlesColumns} dataSource={currentSocialHandles} />
{displayOther && otherField}

); };