/* eslint-disable react/no-unescaped-entities */ import { Typography, Modal, Button, Row, Col, Alert } from 'antd'; import React, { ReactElement, useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { TEXTFIELD_TYPE_TEXT, TEXTFIELD_TYPE_TEXTAREA, TEXTFIELD_TYPE_URL, } from '../../components/admin/TextField'; import { TextFieldWithSubmit } from '../../components/admin/TextFieldWithSubmit'; import { ToggleSwitch } from '../../components/admin/ToggleSwitch'; import { EditValueArray } from '../../components/admin/EditValueArray'; import { UpdateArgs } from '../../types/config-section'; import { FIELD_PROPS_ENABLE_FEDERATION, TEXTFIELD_PROPS_FEDERATION_LIVE_MESSAGE, TEXTFIELD_PROPS_FEDERATION_DEFAULT_USER, FIELD_PROPS_FEDERATION_IS_PRIVATE, FIELD_PROPS_SHOW_FEDERATION_ENGAGEMENT, TEXTFIELD_PROPS_FEDERATION_INSTANCE_URL, FIELD_PROPS_FEDERATION_BLOCKED_DOMAINS, postConfigUpdateToAPI, RESET_TIMEOUT, API_FEDERATION_BLOCKED_DOMAINS, FIELD_PROPS_FEDERATION_NSFW, } from '../../utils/config-constants'; import { ServerStatusContext } from '../../utils/server-status-context'; import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses'; import { AdminLayout } from '../../components/layouts/AdminLayout'; const FederationInfoModal = ({ cancelPressed, okPressed }) => ( } > How do Owncast's social features work? Owncast's social features are accomplished by having your server join The{' '} Fediverse , a decentralized, open, collection of independent servers, like yours. Please{' '} read more {' '} about these features, the details behind them, and how they work. What do you need to know? Learn more about The Fediverse If these concepts are new you should discover more about what this functionality has to offer. Visit{' '} our documentation {' '} to be pointed at some resources that will help get you started on The Fediverse. ); FederationInfoModal.propTypes = { cancelPressed: PropTypes.func.isRequired, okPressed: PropTypes.func.isRequired, }; const ConfigFederation = () => { const { Title } = Typography; const [formDataValues, setFormDataValues] = useState(null); const [isInfoModalOpen, setIsInfoModalOpen] = useState(false); const serverStatusData = useContext(ServerStatusContext); const { serverConfig, setFieldInConfigState } = serverStatusData || {}; const [blockedDomainSaveState, setBlockedDomainSaveState] = useState(null); const { federation, yp, instanceDetails } = serverConfig; const { enabled, isPrivate, username, goLiveMessage, showEngagement, blockedDomains } = federation; const { instanceUrl } = yp; const { nsfw } = instanceDetails; const handleFieldChange = ({ fieldName, value }: UpdateArgs) => { setFormDataValues({ ...formDataValues, [fieldName]: value, }); }; const handleEnabledSwitchChange = (value: boolean) => { if (!value) { setFormDataValues({ ...formDataValues, enabled: false, }); } else { setIsInfoModalOpen(true); } }; // if instanceUrl is empty, we should also turn OFF the `enabled` field of directory. const handleSubmitInstanceUrl = () => { const hasInstanceUrl = formDataValues.instanceUrl !== ''; const isInstanceUrlSecure = formDataValues.instanceUrl.startsWith('https://'); if (!hasInstanceUrl || !isInstanceUrlSecure) { postConfigUpdateToAPI({ apiPath: FIELD_PROPS_ENABLE_FEDERATION.apiPath, data: { value: false }, }); setFormDataValues({ ...formDataValues, enabled: false, }); } }; function federationInfoModalCancelPressed() { setIsInfoModalOpen(false); setFormDataValues({ ...formDataValues, enabled: false, }); } function federationInfoModalOkPressed() { setIsInfoModalOpen(false); setFormDataValues({ ...formDataValues, enabled: true, }); } function resetBlockedDomainsSaveState() { setBlockedDomainSaveState(null); } function saveBlockedDomains() { try { postConfigUpdateToAPI({ apiPath: API_FEDERATION_BLOCKED_DOMAINS, data: { value: formDataValues.blockedDomains }, onSuccess: () => { setFieldInConfigState({ fieldName: 'forbiddenUsernames', value: formDataValues.forbiddenUsernames, }); setBlockedDomainSaveState(STATUS_SUCCESS); setTimeout(resetBlockedDomainsSaveState, RESET_TIMEOUT); }, onError: (message: string) => { setBlockedDomainSaveState(createInputStatus(STATUS_ERROR, message)); setTimeout(resetBlockedDomainsSaveState, RESET_TIMEOUT); }, }); } catch (e) { console.error(e); setBlockedDomainSaveState(STATUS_ERROR); } } function handleDeleteBlockedDomain(index: number) { formDataValues.blockedDomains.splice(index, 1); saveBlockedDomains(); } function handleCreateBlockedDomain(domain: string) { let newDomain; try { const u = new URL(domain); newDomain = u.host; } catch (_) { newDomain = domain; } formDataValues.blockedDomains.push(newDomain); handleFieldChange({ fieldName: 'blockedDomains', value: formDataValues.blockedDomains, }); saveBlockedDomains(); } useEffect(() => { setFormDataValues({ enabled, isPrivate, username, goLiveMessage, showEngagement, blockedDomains, nsfw, instanceUrl: yp.instanceUrl, }); }, [serverConfig, yp]); if (!formDataValues) { return null; } const hasInstanceUrl = instanceUrl !== ''; const isInstanceUrlSecure = instanceUrl.startsWith('https://'); const configurationWarning = !isInstanceUrlSecure && ( <>
); const invalidPortWarning = ( ); const hasInvalidPort = instanceUrl && new URL(instanceUrl).port !== '' && new URL(instanceUrl).port !== '443'; return (
Configure Social Features

Owncast provides the ability for people to follow and engage with your instance. It's a great way to promote alerting, sharing and engagement of your stream.

Once enabled you'll alert your followers when you go live as well as gain the ability to compose custom posts to share any information you like.

Read more about the specifics of these social features.

{configurationWarning} {hasInvalidPort && invalidPortWarning} {isInfoModalOpen && ( )}
); }; ConfigFederation.getLayout = function getLayout(page: ReactElement) { return ; }; export default ConfigFederation;