import React, { useState, useContext, useEffect } from 'react'; import { Typography, Table, Button, Modal, Input } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import dynamic from 'next/dynamic'; 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 { isValidMatrixAccount, isValidAccount, isValidUrl, DEFAULT_TEXTFIELD_URL_PATTERN, } from '../../../../utils/validators'; import { TextField } from '../../TextField'; import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../../../utils/input-statuses'; import { FormStatusIndicator } from '../../FormStatusIndicator'; const { Title } = Typography; // Lazy loaded components const CaretDownOutlined = dynamic(() => import('@ant-design/icons/CaretDownOutlined'), { ssr: false, }); const CaretUpOutlined = dynamic(() => import('@ant-design/icons/CaretUpOutlined'), { ssr: false, }); const DeleteOutlined = dynamic(() => import('@ant-design/icons/DeleteOutlined'), { ssr: false, }); // eslint-disable-next-line react/function-component-definition export default function EditSocialLinks() { 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: (_, 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: (_, _record, index) => (
), }, ]; const isValid = (url: string, platform: string) => { if (platform === 'xmpp') { return isValidAccount(url, 'xmpp'); } if (platform === 'matrix') { return isValidMatrixAccount(url); } return isValidUrl(url); }; const okButtonProps = { disabled: !isValid(modalDataState.url, modalDataState.platform), }; 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}

); }