owncast/web/components/config/edit-social-links.tsx

320 lines
9.3 KiB
TypeScript
Raw Normal View History

2021-01-19 21:34:06 +03:00
import React, { useState, useContext, useEffect } from 'react';
2021-01-24 12:22:28 +03:00
import { Typography, Table, Button, Modal, Input } from 'antd';
2021-01-24 07:16:01 +03:00
import { ColumnsType } from 'antd/lib/table';
import { DeleteOutlined } from '@ant-design/icons';
import SocialDropdown from './social-icons-dropdown';
import { fetchData, SOCIAL_PLATFORMS_LIST, NEXT_PUBLIC_API_HOST } from '../../utils/apis';
import { ServerStatusContext } from '../../utils/server-status-context';
2021-01-31 12:38:20 +03:00
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';
2021-02-01 09:07:00 +03:00
import TextField from './form-textfield';
import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses';
import FormStatusIndicator from './form-status-indicator';
2021-01-19 21:34:06 +03:00
const { Title } = Typography;
export default function EditSocialLinks() {
2021-01-19 21:34:06 +03:00
const [availableIconsList, setAvailableIconsList] = useState([]);
const [currentSocialHandles, setCurrentSocialHandles] = useState([]);
2021-01-24 07:16:01 +03:00
const [displayModal, setDisplayModal] = useState(false);
2021-01-24 12:22:28 +03:00
const [displayOther, setDisplayOther] = useState(false);
2021-01-24 07:16:01 +03:00
const [modalProcessing, setModalProcessing] = useState(false);
2021-01-24 12:22:28 +03:00
const [editId, setEditId] = useState(-1);
2021-01-31 12:38:20 +03:00
2021-01-24 07:16:01 +03:00
// current data inside modal
const [modalDataState, setModalDataState] = useState(DEFAULT_SOCIAL_HANDLE);
const [submitStatus, setSubmitStatus] = useState(null);
2021-01-19 21:34:06 +03:00
const serverStatusData = useContext(ServerStatusContext);
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
const { instanceDetails } = serverConfig;
const { socialHandles: initialSocialHandles } = instanceDetails;
2021-01-24 07:16:01 +03:00
let resetTimer = null;
const PLACEHOLDERS = {
mastodon: 'https://mastodon.social/@username',
twitter: 'https://twitter.com/username',
};
2021-01-19 21:34:06 +03:00
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) {
2021-01-31 12:38:20 +03:00
console.log(error);
2021-01-19 21:34:06 +03:00
// do nothing
}
};
const isPredefinedSocial = (platform: string) =>
availableIconsList.find(item => item.key === platform) || false;
2021-01-31 12:38:20 +03:00
const selectedOther =
modalDataState.platform !== '' &&
!availableIconsList.find(item => item.key === modalDataState.platform);
2021-01-24 12:22:28 +03:00
2021-01-19 21:34:06 +03:00
useEffect(() => {
getAvailableIcons();
}, []);
useEffect(() => {
2021-01-24 12:22:28 +03:00
if (instanceDetails.socialHandles) {
setCurrentSocialHandles(initialSocialHandles);
}
2021-01-19 21:34:06 +03:00
}, [instanceDetails]);
2021-01-24 07:16:01 +03:00
const resetStates = () => {
setSubmitStatus(null);
resetTimer = null;
clearTimeout(resetTimer);
2021-01-24 12:22:28 +03:00
};
const resetModal = () => {
2021-01-24 07:16:01 +03:00
setDisplayModal(false);
setEditId(-1);
2021-01-24 12:22:28 +03:00
setDisplayOther(false);
setModalProcessing(false);
2021-01-31 12:38:20 +03:00
setModalDataState({ ...DEFAULT_SOCIAL_HANDLE });
2021-01-24 12:22:28 +03:00
};
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);
};
2021-02-01 09:07:00 +03:00
const handleUrlChange = ({ value }: UpdateArgs) => {
2021-01-24 12:22:28 +03:00
updateModalState('url', value);
};
2021-01-24 07:16:01 +03:00
// 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: () => {
2021-01-31 12:38:20 +03:00
setFieldInConfigState({
fieldName: 'socialHandles',
value: postValue,
path: 'instanceDetails',
});
2021-01-24 07:16:01 +03:00
// close modal
setModalProcessing(false);
handleModalCancel();
2021-02-01 09:07:00 +03:00
setSubmitStatus(createInputStatus(STATUS_SUCCESS));
2021-01-24 07:16:01 +03:00
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
},
onError: (message: string) => {
2021-02-01 09:07:00 +03:00
setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${message}`));
setModalProcessing(false);
2021-01-24 07:16:01 +03:00
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
},
});
};
// on Ok, send all of dataState to api
// show loading
// close modal when api is done
const handleModalOk = () => {
2021-01-31 12:38:20 +03:00
setModalProcessing(true);
const postData = currentSocialHandles.length ? [...currentSocialHandles] : [];
2021-01-24 07:16:01 +03:00
if (editId === -1) {
postData.push(modalDataState);
} else {
postData.splice(editId, 1, modalDataState);
}
postUpdateToAPI(postData);
};
const handleDeleteItem = (index: number) => {
2021-01-31 12:38:20 +03:00
const postData = [...currentSocialHandles];
2021-01-24 07:16:01 +03:00
postData.splice(index, 1);
2021-01-24 12:22:28 +03:00
postUpdateToAPI(postData);
2021-01-24 07:16:01 +03:00
};
2021-01-31 12:38:20 +03:00
const socialHandlesColumns: ColumnsType<SocialHandle> = [
2021-01-24 07:16:01 +03:00
{
title: 'Social Link',
dataIndex: '',
key: 'combo',
render: (data, record) => {
const { platform, url } = record;
const platformInfo = isPredefinedSocial(platform);
// custom platform case
2021-01-24 07:16:01 +03:00
if (!platformInfo) {
return (
<div className="social-handle-cell">
<p className="option-label">
<strong>{platform}</strong>
2021-05-23 09:28:21 +03:00
<span className="handle-url" title={url}>
{url}
</span>
</p>
</div>
);
2021-01-24 07:16:01 +03:00
}
const { icon, platform: platformName } = platformInfo;
const iconUrl = `${NEXT_PUBLIC_API_HOST}${icon.slice(1)}`;
2021-01-24 07:16:01 +03:00
return (
<div className="social-handle-cell">
2021-01-24 07:16:01 +03:00
<span className="option-icon">
2021-05-23 09:28:21 +03:00
<img src={iconUrl} alt="" className="option-icon" />
2021-01-24 07:16:01 +03:00
</span>
<p className="option-label">
<strong>{platformName}</strong>
2021-05-23 09:28:21 +03:00
<span className="handle-url" title={url}>
{url}
</span>
</p>
</div>
2021-01-24 07:16:01 +03:00
);
},
},
{
title: '',
dataIndex: '',
key: 'edit',
render: (data, record, index) => (
<div className="actions">
<Button
size="small"
onClick={() => {
const platformInfo = currentSocialHandles[index];
setEditId(index);
setModalDataState({ ...platformInfo });
setDisplayModal(true);
if (!isPredefinedSocial(platformInfo.platform)) {
setDisplayOther(true);
}
}}
>
Edit
</Button>
<Button
className="delete-button"
icon={<DeleteOutlined />}
size="small"
onClick={() => handleDeleteItem(index)}
/>
</div>
),
2021-01-31 12:38:20 +03:00
},
];
const okButtonProps = {
2021-01-31 12:38:20 +03:00
disabled: !isValidUrl(modalDataState.url),
};
const otherField = (
<div className="other-field-container formfield-container">
<div className="label-side" />
<div className="input-side">
<Input
placeholder="Other platform name"
defaultValue={modalDataState.platform}
onChange={handleOtherNameChange}
/>
</div>
</div>
);
2021-01-19 21:34:06 +03:00
return (
2021-02-01 09:07:00 +03:00
<div className="social-links-edit-container">
<Title level={3} className="section-title">
Your Social Handles
</Title>
<p className="description">
Add all your social media handles and links to your other profiles here.
</p>
2021-01-24 07:16:01 +03:00
<FormStatusIndicator status={submitStatus} />
2021-01-24 07:16:01 +03:00
<Table
className="social-handles-table"
2021-01-24 07:16:01 +03:00
pagination={false}
size="small"
rowKey={record => `${record.platform}-${record.url}`}
2021-01-24 07:16:01 +03:00
columns={socialHandlesColumns}
dataSource={currentSocialHandles}
/>
<Modal
title="Edit Social Handle"
visible={displayModal}
onOk={handleModalOk}
onCancel={handleModalCancel}
confirmLoading={modalProcessing}
okButtonProps={okButtonProps}
2021-01-24 07:16:01 +03:00
>
<div className="social-handle-modal-content">
<SocialDropdown
iconList={availableIconsList}
selectedOption={selectedOther ? OTHER_SOCIAL_HANDLE_OPTION : modalDataState.platform}
onSelected={handleDropdownSelect}
/>
{displayOther && otherField}
<br />
<TextField
fieldName="social-url"
label="URL"
placeholder={PLACEHOLDERS[modalDataState.platform] || 'Url to page'}
value={modalDataState.url}
onChange={handleUrlChange}
useTrim
type="url"
pattern={DEFAULT_TEXTFIELD_URL_PATTERN}
/>
<FormStatusIndicator status={submitStatus} />
</div>
2021-01-24 07:16:01 +03:00
</Modal>
<br />
2021-01-31 12:38:20 +03:00
<Button
type="primary"
onClick={() => {
2021-01-24 12:22:28 +03:00
resetModal();
2021-01-24 07:16:01 +03:00
setDisplayModal(true);
2021-01-31 12:38:20 +03:00
}}
>
2021-01-24 12:22:28 +03:00
Add a new social link
2021-01-24 07:16:01 +03:00
</Button>
2021-01-19 21:34:06 +03:00
</div>
2021-01-31 12:38:20 +03:00
);
2021-01-19 21:34:06 +03:00
}