mirror of
https://github.com/owncast/owncast.git
synced 2024-11-26 23:24:29 +03:00
Merge branch '0.0.6' of github.com:owncast/owncast-admin into 0.0.6
This commit is contained in:
commit
5b88b068ed
40 changed files with 1150 additions and 552 deletions
1
web/.github/workflows/linter.yml
vendored
1
web/.github/workflows/linter.yml
vendored
|
@ -20,4 +20,3 @@ jobs:
|
|||
config-path: '.eslintrc.js'
|
||||
ignore-path: '.eslintignore'
|
||||
extensions: 'ts,tsx,js,jsx'
|
||||
extra-args: '--max-warnings=0'
|
|
@ -19,8 +19,11 @@ const TOOLTIPS = {
|
|||
4: 'high',
|
||||
5: 'highest',
|
||||
};
|
||||
|
||||
export default function CPUUsageSelector({ defaultValue, onChange }) {
|
||||
interface Props {
|
||||
defaultValue: number;
|
||||
onChange: (arg: number) => void;
|
||||
}
|
||||
export default function CPUUsageSelector({ defaultValue, onChange }: Props) {
|
||||
const [selectedOption, setSelectedOption] = useState(null);
|
||||
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
|
@ -42,10 +45,14 @@ export default function CPUUsageSelector({ defaultValue, onChange }) {
|
|||
|
||||
return (
|
||||
<div className="config-video-segements-conatiner">
|
||||
<Title level={3}>CPU Usage</Title>
|
||||
<p>There are trade-offs when considering CPU usage blah blah more wording here.</p>
|
||||
<br />
|
||||
<Title level={3} className="section-title">
|
||||
CPU Usage
|
||||
</Title>
|
||||
<p className="description">
|
||||
There are trade-offs when considering CPU usage blah blah more wording here.
|
||||
</p>
|
||||
<br />
|
||||
|
||||
<div className="segment-slider-container">
|
||||
<Slider
|
||||
tipFormatter={value => TOOLTIPS[value]}
|
||||
|
|
|
@ -33,9 +33,11 @@ export default function EditYPDetails() {
|
|||
}
|
||||
return (
|
||||
<div className="config-directory-details-form">
|
||||
<Title level={3}>Owncast Directory Settings</Title>
|
||||
<Title level={3} className="section-title">
|
||||
Owncast Directory Settings
|
||||
</Title>
|
||||
|
||||
<p>
|
||||
<p className="description">
|
||||
Would you like to appear in the{' '}
|
||||
<a href="https://directory.owncast.online" target="_blank" rel="noreferrer">
|
||||
<strong>Owncast Directory</strong>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { Typography } from 'antd';
|
||||
|
||||
import TextFieldWithSubmit, {
|
||||
TEXTFIELD_TYPE_TEXTAREA,
|
||||
TEXTFIELD_TYPE_URL,
|
||||
|
@ -12,9 +14,14 @@ import {
|
|||
TEXTFIELD_PROPS_SERVER_SUMMARY,
|
||||
TEXTFIELD_PROPS_LOGO,
|
||||
API_YP_SWITCH,
|
||||
FIELD_PROPS_YP,
|
||||
FIELD_PROPS_NSFW,
|
||||
} from '../../utils/config-constants';
|
||||
|
||||
import { UpdateArgs } from '../../types/config-section';
|
||||
import ToggleSwitch from './form-toggleswitch-with-submit';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function EditInstanceDetails() {
|
||||
const [formDataValues, setFormDataValues] = useState(null);
|
||||
|
@ -22,6 +29,7 @@ export default function EditInstanceDetails() {
|
|||
const { serverConfig } = serverStatusData || {};
|
||||
|
||||
const { instanceDetails, yp } = serverConfig;
|
||||
const { instanceUrl } = yp;
|
||||
|
||||
useEffect(() => {
|
||||
setFormDataValues({
|
||||
|
@ -53,40 +61,74 @@ export default function EditInstanceDetails() {
|
|||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`publicDetailsContainer`}>
|
||||
<div className={`textFieldsSection`}>
|
||||
<TextFieldWithSubmit
|
||||
fieldName="instanceUrl"
|
||||
{...TEXTFIELD_PROPS_INSTANCE_URL}
|
||||
value={formDataValues.instanceUrl}
|
||||
initialValue={yp.instanceUrl}
|
||||
type={TEXTFIELD_TYPE_URL}
|
||||
onChange={handleFieldChange}
|
||||
onSubmit={handleSubmitInstanceUrl}
|
||||
/>
|
||||
const hasInstanceUrl = instanceUrl !== '';
|
||||
|
||||
<TextFieldWithSubmit
|
||||
fieldName="name"
|
||||
{...TEXTFIELD_PROPS_SERVER_NAME}
|
||||
value={formDataValues.name}
|
||||
initialValue={instanceDetails.name}
|
||||
onChange={handleFieldChange}
|
||||
return (
|
||||
<div className="edit-general-settings">
|
||||
<Title level={3} className="section-title">
|
||||
Configure Instance Details
|
||||
</Title>
|
||||
<br />
|
||||
|
||||
<TextFieldWithSubmit
|
||||
fieldName="instanceUrl"
|
||||
{...TEXTFIELD_PROPS_INSTANCE_URL}
|
||||
value={formDataValues.instanceUrl}
|
||||
initialValue={yp.instanceUrl}
|
||||
type={TEXTFIELD_TYPE_URL}
|
||||
onChange={handleFieldChange}
|
||||
onSubmit={handleSubmitInstanceUrl}
|
||||
/>
|
||||
|
||||
<TextFieldWithSubmit
|
||||
fieldName="name"
|
||||
{...TEXTFIELD_PROPS_SERVER_NAME}
|
||||
value={formDataValues.name}
|
||||
initialValue={instanceDetails.name}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<TextFieldWithSubmit
|
||||
fieldName="summary"
|
||||
{...TEXTFIELD_PROPS_SERVER_SUMMARY}
|
||||
type={TEXTFIELD_TYPE_TEXTAREA}
|
||||
value={formDataValues.summary}
|
||||
initialValue={instanceDetails.summary}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<TextFieldWithSubmit
|
||||
fieldName="logo"
|
||||
{...TEXTFIELD_PROPS_LOGO}
|
||||
value={formDataValues.logo}
|
||||
initialValue={instanceDetails.logo}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
<Title level={3} className="section-title">
|
||||
Owncast Directory Settings
|
||||
</Title>
|
||||
<p className="description">
|
||||
Would you like to appear in the{' '}
|
||||
<a href="https://directory.owncast.online" target="_blank" rel="noreferrer">
|
||||
<strong>Owncast Directory</strong>
|
||||
</a>
|
||||
?
|
||||
</p>
|
||||
<div className="config-yp-container">
|
||||
<ToggleSwitch
|
||||
fieldName="enabled"
|
||||
useSubmit
|
||||
{...FIELD_PROPS_YP}
|
||||
checked={formDataValues.enabled}
|
||||
disabled={!hasInstanceUrl}
|
||||
/>
|
||||
<TextFieldWithSubmit
|
||||
fieldName="summary"
|
||||
{...TEXTFIELD_PROPS_SERVER_SUMMARY}
|
||||
type={TEXTFIELD_TYPE_TEXTAREA}
|
||||
value={formDataValues.summary}
|
||||
initialValue={instanceDetails.summary}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<TextFieldWithSubmit
|
||||
fieldName="logo"
|
||||
{...TEXTFIELD_PROPS_LOGO}
|
||||
value={formDataValues.logo}
|
||||
initialValue={instanceDetails.logo}
|
||||
onChange={handleFieldChange}
|
||||
<ToggleSwitch
|
||||
fieldName="nsfw"
|
||||
useSubmit
|
||||
{...FIELD_PROPS_NSFW}
|
||||
checked={formDataValues.nsfw}
|
||||
disabled={!hasInstanceUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,33 +1,34 @@
|
|||
// EDIT CUSTOM DETAILS ON YOUR PAGE
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { Typography, Button } from 'antd';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
|
||||
import { ServerStatusContext } from '../utils/server-status-context';
|
||||
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||
import {
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
API_CUSTOM_CONTENT,
|
||||
} from '../utils/config-constants';
|
||||
} from '../../utils/config-constants';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_PROCESSING,
|
||||
STATUS_SUCCESS,
|
||||
} from '../utils/input-statuses';
|
||||
import 'react-markdown-editor-lite/lib/index.css';
|
||||
import FormStatusIndicator from '../components/config/form-status-indicator';
|
||||
} from '../../utils/input-statuses';
|
||||
import FormStatusIndicator from './form-status-indicator';
|
||||
|
||||
const { Title } = Typography;
|
||||
import 'react-markdown-editor-lite/lib/index.css';
|
||||
|
||||
const mdParser = new MarkdownIt(/* Markdown-it options */);
|
||||
|
||||
const MdEditor = dynamic(() => import('react-markdown-editor-lite'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function PageContentEditor() {
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function EditPageContent() {
|
||||
const [content, setContent] = useState('');
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
const [hasChanged, setHasChanged] = useState(false);
|
||||
|
@ -83,10 +84,12 @@ export default function PageContentEditor() {
|
|||
}, [instanceDetails]);
|
||||
|
||||
return (
|
||||
<div className="config-page-content-form">
|
||||
<Title level={2}>Page Content</Title>
|
||||
<div className="edit-page-content">
|
||||
<Title level={3} className="section-title">
|
||||
Custom Page Content
|
||||
</Title>
|
||||
|
||||
<p>
|
||||
<p className="description">
|
||||
Edit the content of your page by using simple{' '}
|
||||
<a href="https://www.markdownguide.org/basic-syntax/">Markdown syntax</a>.
|
||||
</p>
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { Button, Tooltip, Collapse, Popconfirm } from 'antd';
|
||||
import { Button, Tooltip, Collapse } from 'antd';
|
||||
import { CopyOutlined, RedoOutlined } from '@ant-design/icons';
|
||||
const { Panel } = Collapse;
|
||||
|
||||
import { TEXTFIELD_TYPE_NUMBER, TEXTFIELD_TYPE_PASSWORD } from './form-textfield';
|
||||
import TextFieldWithSubmit from './form-textfield-with-submit';
|
||||
|
@ -15,9 +14,11 @@ import {
|
|||
TEXTFIELD_PROPS_STREAM_KEY,
|
||||
TEXTFIELD_PROPS_WEB_PORT,
|
||||
} from '../../utils/config-constants';
|
||||
import { fetchData, API_YP_RESET } from '../../utils/apis';
|
||||
|
||||
import { UpdateArgs } from '../../types/config-section';
|
||||
import ResetYP from './reset-yp';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
||||
export default function EditInstanceDetails() {
|
||||
const [formDataValues, setFormDataValues] = useState(null);
|
||||
|
@ -68,41 +69,6 @@ export default function EditInstanceDetails() {
|
|||
}
|
||||
};
|
||||
|
||||
const resetDirectoryRegistration = async () => {
|
||||
try {
|
||||
await fetchData(API_YP_RESET);
|
||||
setMessage('');
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
|
||||
function ResetYP() {
|
||||
// TODO: Uncomment this after it's styled.
|
||||
// if (yp.enabled) {
|
||||
return (
|
||||
<div className="field-container">
|
||||
Reset Directory:
|
||||
<Popconfirm
|
||||
placement="topLeft"
|
||||
title={'Are you sure you want to reset your connection to the Owncast directory?'}
|
||||
onConfirm={resetDirectoryRegistration}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button>Reset Directory Connection</Button>
|
||||
</Popconfirm>
|
||||
<p>
|
||||
If you are experiencing issues with your listing on the Owncast Directory and were asked
|
||||
to "reset" your connection to the service, you can do that here. The next time you go live
|
||||
it will try and re-register your server with the directory from scratch.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
// }
|
||||
// return null;
|
||||
}
|
||||
|
||||
function generateStreamKey() {
|
||||
let key = '';
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
|
@ -172,13 +138,14 @@ export default function EditInstanceDetails() {
|
|||
onChange={handleFieldChange}
|
||||
onSubmit={showConfigurationRestartMessage}
|
||||
/>
|
||||
<Collapse>
|
||||
<Panel header="Advanced Settings" key="1">
|
||||
<div className="form-fields">
|
||||
|
||||
{yp.enabled && (
|
||||
<Collapse className="advanced-settings">
|
||||
<Panel header="Advanced Settings" key="1">
|
||||
<ResetYP />
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -165,43 +165,36 @@ export default function EditSocialLinks() {
|
|||
|
||||
const socialHandlesColumns: ColumnsType<SocialHandle> = [
|
||||
{
|
||||
title: '#',
|
||||
dataIndex: 'key',
|
||||
key: 'key',
|
||||
},
|
||||
{
|
||||
title: 'Platform',
|
||||
dataIndex: 'platform',
|
||||
key: 'platform',
|
||||
render: (platform: string) => {
|
||||
title: 'Social Link',
|
||||
dataIndex: '',
|
||||
key: 'combo',
|
||||
render: (data, record) => {
|
||||
const { platform, url } = record;
|
||||
const platformInfo = availableIconsList.find(item => item.key === platform);
|
||||
if (!platformInfo) {
|
||||
return platform;
|
||||
}
|
||||
const { icon, platform: platformName } = platformInfo;
|
||||
return (
|
||||
<>
|
||||
<div className="social-handle-cell">
|
||||
<span className="option-icon">
|
||||
<img src={`${NEXT_PUBLIC_API_HOST}${icon}`} alt="" className="option-icon" />
|
||||
</span>
|
||||
<span className="option-label">{platformName}</span>
|
||||
</>
|
||||
<p className="option-label">
|
||||
<strong>{platformName}</strong>
|
||||
<span>{url}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Url Link',
|
||||
dataIndex: 'url',
|
||||
key: 'url',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
dataIndex: '',
|
||||
key: 'edit',
|
||||
render: (data, record, index) => {
|
||||
return (
|
||||
<span className="actions">
|
||||
<div className="actions">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
|
@ -219,7 +212,7 @@ export default function EditSocialLinks() {
|
|||
size="small"
|
||||
onClick={() => handleDeleteItem(index)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
@ -231,12 +224,17 @@ export default function EditSocialLinks() {
|
|||
|
||||
return (
|
||||
<div className="social-links-edit-container">
|
||||
<p>Add all your social media handles and links to your other profiles here.</p>
|
||||
<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>
|
||||
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
|
||||
<Table
|
||||
className="dataTable"
|
||||
className="social-handles-table"
|
||||
pagination={false}
|
||||
size="small"
|
||||
rowKey={record => record.url}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
import TextField from './form-textfield';
|
||||
import FormStatusIndicator from './form-status-indicator';
|
||||
import { isValidUrl } from '../../utils/urls';
|
||||
// import ToggleSwitch from './form-toggleswitch-with-submit';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
||||
|
@ -135,6 +136,7 @@ export default function EditStorage() {
|
|||
|
||||
const containerClass = classNames({
|
||||
'edit-storage-container': true,
|
||||
'form-module': true,
|
||||
enabled: shouldDisplayForm,
|
||||
});
|
||||
|
||||
|
@ -143,6 +145,12 @@ export default function EditStorage() {
|
|||
return (
|
||||
<div className={containerClass}>
|
||||
<div className="enable-switch">
|
||||
{/* <ToggleSwitch
|
||||
fieldName="enabled"
|
||||
label="Storage Enabled"
|
||||
checked={formDataValues.enabled}
|
||||
onChange={handleSwitchChange}
|
||||
/> */}
|
||||
<Switch
|
||||
checked={formDataValues.enabled}
|
||||
defaultChecked={formDataValues.enabled}
|
||||
|
|
|
@ -21,6 +21,8 @@ import {
|
|||
|
||||
const { Title } = Typography;
|
||||
|
||||
const TAG_COLOR = '#5a67d8';
|
||||
|
||||
export default function EditInstanceTags() {
|
||||
const [newTagInput, setNewTagInput] = useState<string>('');
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
@ -100,8 +102,12 @@ export default function EditInstanceTags() {
|
|||
|
||||
return (
|
||||
<div className="tag-editor-container">
|
||||
<Title level={3}>Add Tags</Title>
|
||||
<p>This is a great way to categorize your Owncast server on the Directory!</p>
|
||||
<Title level={3} className="section-title">
|
||||
Add Tags
|
||||
</Title>
|
||||
<p className="description">
|
||||
This is a great way to categorize your Owncast server on the Directory!
|
||||
</p>
|
||||
|
||||
<div className="tag-current-tags">
|
||||
{tags.map((tag, index) => {
|
||||
|
@ -109,7 +115,7 @@ export default function EditInstanceTags() {
|
|||
handleDeleteTag(index);
|
||||
};
|
||||
return (
|
||||
<Tag closable onClose={handleClose} key={`tag-${tag}-${index}`}>
|
||||
<Tag closable onClose={handleClose} color={TAG_COLOR} key={`tag-${tag}-${index}`}>
|
||||
{tag}
|
||||
</Tag>
|
||||
);
|
||||
|
|
|
@ -120,7 +120,7 @@ export default function TextFieldWithSubmit(props: TextFieldWithSubmitProps) {
|
|||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="textfield-container lower-container">
|
||||
<div className="formfield-container lower-container">
|
||||
<p className="label-spacer" />
|
||||
<div className="lower-content">
|
||||
<div className="field-tip">{tip}</div>
|
||||
|
|
|
@ -108,6 +108,7 @@ export default function TextField(props: TextFieldProps) {
|
|||
const { type: statusType } = status || {};
|
||||
|
||||
const containerClass = classNames({
|
||||
'formfield-container': true,
|
||||
'textfield-container': true,
|
||||
[`type-${type}`]: true,
|
||||
required,
|
||||
|
@ -117,7 +118,7 @@ export default function TextField(props: TextFieldProps) {
|
|||
<div className={containerClass}>
|
||||
{label ? (
|
||||
<div className="label-side">
|
||||
<label htmlFor={fieldId} className="textfield-label">
|
||||
<label htmlFor={fieldId} className="formfield-label">
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
|
@ -140,10 +141,7 @@ export default function TextField(props: TextFieldProps) {
|
|||
/>
|
||||
</div>
|
||||
<FormStatusIndicator status={status} />
|
||||
<p className="field-tip">
|
||||
{tip}
|
||||
{/* <InfoTip tip={tip} /> */}
|
||||
</p>
|
||||
<p className="field-tip">{tip}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -151,9 +149,7 @@ export default function TextField(props: TextFieldProps) {
|
|||
|
||||
TextField.defaultProps = {
|
||||
className: '',
|
||||
// configPath: '',
|
||||
disabled: false,
|
||||
// initialValue: '',
|
||||
label: '',
|
||||
maxLength: 255,
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// This is a wrapper for the Ant Switch component.
|
||||
// onChange of the switch, it will automatically post a change to the config api.
|
||||
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { Switch } from 'antd';
|
||||
import {
|
||||
|
@ -12,7 +15,6 @@ import FormStatusIndicator from './form-status-indicator';
|
|||
import { RESET_TIMEOUT, postConfigUpdateToAPI } from '../../utils/config-constants';
|
||||
|
||||
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||
import InfoTip from '../info-tip';
|
||||
|
||||
interface ToggleSwitchProps {
|
||||
apiPath: string;
|
||||
|
@ -23,8 +25,9 @@ interface ToggleSwitchProps {
|
|||
disabled?: boolean;
|
||||
label?: string;
|
||||
tip?: string;
|
||||
useSubmit?: boolean;
|
||||
onChange?: (arg: boolean) => void;
|
||||
}
|
||||
|
||||
export default function ToggleSwitch(props: ToggleSwitchProps) {
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
|
@ -33,7 +36,17 @@ export default function ToggleSwitch(props: ToggleSwitchProps) {
|
|||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { setFieldInConfigState } = serverStatusData || {};
|
||||
|
||||
const { apiPath, checked, configPath = '', disabled = false, fieldName, label, tip } = props;
|
||||
const {
|
||||
apiPath,
|
||||
checked,
|
||||
configPath = '',
|
||||
disabled = false,
|
||||
fieldName,
|
||||
label,
|
||||
tip,
|
||||
useSubmit,
|
||||
onChange,
|
||||
} = props;
|
||||
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
|
@ -42,41 +55,52 @@ export default function ToggleSwitch(props: ToggleSwitchProps) {
|
|||
};
|
||||
|
||||
const handleChange = async (isChecked: boolean) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
|
||||
if (useSubmit) {
|
||||
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath,
|
||||
data: { value: isChecked },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({ fieldName, value: isChecked, path: configPath });
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS));
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${message}`));
|
||||
},
|
||||
});
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath,
|
||||
data: { value: isChecked },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({ fieldName, value: isChecked, path: configPath });
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS));
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${message}`));
|
||||
},
|
||||
});
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
}
|
||||
if (onChange) {
|
||||
onChange(isChecked);
|
||||
}
|
||||
};
|
||||
|
||||
const loading = submitStatus !== null && submitStatus.type === STATUS_PROCESSING;
|
||||
return (
|
||||
<div className="toggleswitch-container">
|
||||
<div className="toggleswitch">
|
||||
<Switch
|
||||
className={`switch field-${fieldName}`}
|
||||
loading={loading}
|
||||
onChange={handleChange}
|
||||
defaultChecked={checked}
|
||||
checked={checked}
|
||||
checkedChildren="ON"
|
||||
unCheckedChildren="OFF"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<span className="label">
|
||||
{label} <InfoTip tip={tip} />
|
||||
</span>
|
||||
<div className="formfield-container toggleswitch-container">
|
||||
{label && (
|
||||
<div className="label-side">
|
||||
<span className="formfield-label">{label}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="input-side">
|
||||
<div className="input-group">
|
||||
<Switch
|
||||
className={`switch field-${fieldName}`}
|
||||
loading={loading}
|
||||
onChange={handleChange}
|
||||
defaultChecked={checked}
|
||||
checked={checked}
|
||||
checkedChildren="ON"
|
||||
unCheckedChildren="OFF"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</div>
|
||||
<p className="field-tip">{tip}</p>
|
||||
</div>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -87,4 +111,6 @@ ToggleSwitch.defaultProps = {
|
|||
disabled: false,
|
||||
label: '',
|
||||
tip: '',
|
||||
useSubmit: false,
|
||||
onChange: null,
|
||||
};
|
||||
|
|
42
web/components/config/reset-yp.tsx
Normal file
42
web/components/config/reset-yp.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { Popconfirm, Button, Typography } from 'antd';
|
||||
import { useContext } from 'react';
|
||||
import { AlertMessageContext } from '../../utils/alert-message-context';
|
||||
|
||||
import { API_YP_RESET, fetchData } from '../../utils/apis';
|
||||
|
||||
export default function ResetYP() {
|
||||
const { setMessage } = useContext(AlertMessageContext);
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
const resetDirectoryRegistration = async () => {
|
||||
try {
|
||||
await fetchData(API_YP_RESET);
|
||||
setMessage('');
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Title level={3} className="section-title">
|
||||
Reset Directory
|
||||
</Title>
|
||||
<p className="description">
|
||||
If you are experiencing issues with your listing on the Owncast Directory and were asked to
|
||||
"reset" your connection to the service, you can do that here. The next time you go
|
||||
live it will try and re-register your server with the directory from scratch.
|
||||
</p>
|
||||
|
||||
<Popconfirm
|
||||
placement="topLeft"
|
||||
title="Are you sure you want to reset your connection to the Owncast directory?"
|
||||
onConfirm={resetDirectoryRegistration}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button type="primary">Reset Directory Connection</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -19,11 +19,11 @@ export default function SocialDropdown({ iconList, selectedOption, onSelected }:
|
|||
const inititalSelected = selectedOption === '' ? null : selectedOption;
|
||||
return (
|
||||
<div className="social-dropdown-container">
|
||||
<p className="">
|
||||
<p className="description">
|
||||
If you are looking for a platform name not on this list, please select Other and type in
|
||||
your own name. A logo will not be provided.
|
||||
</p>
|
||||
<p className="">
|
||||
<p className="description">
|
||||
If you DO have a logo, drop it in to the <code>/webroot/img/platformicons</code> directory
|
||||
and update the <code>/socialHandle.go</code> list. Then restart the server and it will show
|
||||
up in the list.
|
||||
|
|
|
@ -46,9 +46,6 @@ function SegmentToolTip({ value }: SegmentToolTipProps) {
|
|||
|
||||
export default function VideoLatency() {
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
// const [submitStatus, setSubmitStatus] = useState(null);
|
||||
// const [submitStatusMessage, setSubmitStatusMessage] = useState('');
|
||||
const [selectedOption, setSelectedOption] = useState(null);
|
||||
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
|
@ -68,7 +65,6 @@ export default function VideoLatency() {
|
|||
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
// setSubmitStatusMessage('');
|
||||
resetTimer = null;
|
||||
clearTimeout(resetTimer);
|
||||
};
|
||||
|
@ -88,8 +84,6 @@ export default function VideoLatency() {
|
|||
});
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Latency buffer level updated.'));
|
||||
|
||||
// setSubmitStatus('success');
|
||||
// setSubmitStatusMessage('Variants updated.');
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
if (serverStatusData.online) {
|
||||
setMessage(
|
||||
|
@ -100,8 +94,6 @@ export default function VideoLatency() {
|
|||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
|
||||
// setSubmitStatus('error');
|
||||
// setSubmitStatusMessage(message);
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
});
|
||||
|
@ -113,15 +105,19 @@ export default function VideoLatency() {
|
|||
|
||||
return (
|
||||
<div className="config-video-segements-conatiner">
|
||||
<Title level={3}>Latency Buffer</Title>
|
||||
<p>
|
||||
While it's natural to want to keep your latency as low as possible, you may experience
|
||||
<Title level={3} className="section-title">
|
||||
Latency Buffer
|
||||
</Title>
|
||||
<p className="description">
|
||||
While it's natural to want to keep your latency as low as possible, you may experience
|
||||
reduced error tolerance and stability in some environments the lower you go.
|
||||
</p>
|
||||
For interactive live streams you may want to experiment with a lower latency, for
|
||||
non-interactive broadcasts you may want to increase it.{' '}
|
||||
<a href="https://owncast.online/docs/encoding#latency-buffer">Read to learn more.</a>
|
||||
<p></p>
|
||||
<p className="description">
|
||||
For interactive live streams you may want to experiment with a lower latency, for
|
||||
non-interactive broadcasts you may want to increase it.{' '}
|
||||
<a href="https://owncast.online/docs/encoding#latency-buffer">Read to learn more.</a>
|
||||
</p>
|
||||
|
||||
<div className="segment-slider-container">
|
||||
<Slider
|
||||
tipFormatter={value => <SegmentToolTip value={SLIDER_COMMENTS[value]} />}
|
||||
|
@ -132,8 +128,8 @@ export default function VideoLatency() {
|
|||
defaultValue={selectedOption}
|
||||
value={selectedOption}
|
||||
/>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</div>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// This content populates the video variant modal, which is spawned from the variants table.
|
||||
import React from 'react';
|
||||
import { Slider, Switch, Collapse } from 'antd';
|
||||
import { Slider, Switch, Collapse, Typography } from 'antd';
|
||||
import { FieldUpdaterFunc, VideoVariant, UpdateArgs } from '../../types/config-section';
|
||||
import TextField from './form-textfield';
|
||||
import { DEFAULT_VARIANT_STATE } from '../../utils/config-constants';
|
||||
import InfoTip from '../info-tip';
|
||||
import CPUUsageSelector from './cpu-usage';
|
||||
// import ToggleSwitch from './form-toggleswitch-with-submit';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
||||
|
@ -55,7 +56,6 @@ const VIDEO_VARIANT_DEFAULTS = {
|
|||
tip: "Optionally resize this content's height.",
|
||||
},
|
||||
};
|
||||
|
||||
interface VideoVariantFormProps {
|
||||
dataState: VideoVariant;
|
||||
onUpdateField: FieldUpdaterFunc;
|
||||
|
@ -79,6 +79,7 @@ export default function VideoVariantForm({
|
|||
};
|
||||
const handleScaledWidthChanged = (args: UpdateArgs) => {
|
||||
const value = Number(args.value);
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
|
@ -86,6 +87,7 @@ export default function VideoVariantForm({
|
|||
};
|
||||
const handleScaledHeightChanged = (args: UpdateArgs) => {
|
||||
const value = Number(args.value);
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
|
@ -108,124 +110,123 @@ export default function VideoVariantForm({
|
|||
|
||||
return (
|
||||
<div className="config-variant-form">
|
||||
<div className="section-intro">
|
||||
<p className="description">
|
||||
Say a thing here about how this all works. Read more{' '}
|
||||
<a href="https://owncast.online/docs/configuration/">here</a>.
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</p>
|
||||
|
||||
{/* ENCODER PRESET FIELD */}
|
||||
<div className="field">
|
||||
<div className="form-component">
|
||||
<CPUUsageSelector
|
||||
defaultValue={dataState.cpuUsageLevel}
|
||||
onChange={handleVideoCpuUsageLevelChange}
|
||||
/>
|
||||
{selectedPresetNote ? (
|
||||
<span className="selected-value-note">{selectedPresetNote}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* VIDEO PASSTHROUGH FIELD */}
|
||||
<div style={{ display: 'none' }}>
|
||||
<div className="field">
|
||||
<p className="label">
|
||||
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.videoPassthrough.tip} />
|
||||
Use Video Passthrough?
|
||||
</p>
|
||||
<div className="form-component">
|
||||
<Switch
|
||||
defaultChecked={dataState.videoPassthrough}
|
||||
checked={dataState.videoPassthrough}
|
||||
onChange={handleVideoPassChange}
|
||||
checkedChildren="Yes"
|
||||
unCheckedChildren="No"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* VIDEO BITRATE FIELD */}
|
||||
<div className={`field ${dataState.videoPassthrough ? 'disabled' : ''}`}>
|
||||
<p className="label">
|
||||
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.videoBitrate.tip} />
|
||||
Video Bitrate:
|
||||
</p>
|
||||
<div className="form-component">
|
||||
<Slider
|
||||
tipFormatter={value => `${value} ${videoBRUnit}`}
|
||||
disabled={dataState.videoPassthrough === true}
|
||||
defaultValue={dataState.videoBitrate}
|
||||
value={dataState.videoBitrate}
|
||||
onChange={handleVideoBitrateChange}
|
||||
step={videoBitrateDefaults.incrementBy}
|
||||
min={videoBRMin}
|
||||
max={videoBRMax}
|
||||
marks={{
|
||||
[videoBRMin]: `${videoBRMin} ${videoBRUnit}`,
|
||||
[videoBRMax]: `${videoBRMax} ${videoBRUnit}`,
|
||||
}}
|
||||
/>
|
||||
{selectedVideoBRnote ? (
|
||||
<span className="selected-value-note">{selectedVideoBRnote}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Collapse>
|
||||
<Panel header="Advanced Settings" key="1">
|
||||
<div className="section-intro">
|
||||
Resizing your content will take additional resources on your server. If you wish to
|
||||
optionally resize your output for this stream variant then you should either set the
|
||||
width <strong>or</strong> the height to keep your aspect ratio.
|
||||
</div>
|
||||
<div className="field">
|
||||
<TextField
|
||||
type="number"
|
||||
{...VIDEO_VARIANT_DEFAULTS.scaledWidth}
|
||||
value={dataState.scaledWidth}
|
||||
onChange={handleScaledWidthChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<TextField
|
||||
type="number"
|
||||
{...VIDEO_VARIANT_DEFAULTS.scaledHeight}
|
||||
value={dataState.scaledHeight}
|
||||
onChange={handleScaledHeightChanged}
|
||||
<div className="row">
|
||||
<div>
|
||||
{/* ENCODER PRESET FIELD */}
|
||||
<div className="form-module cpu-usage-container">
|
||||
<CPUUsageSelector
|
||||
defaultValue={dataState.cpuUsageLevel}
|
||||
onChange={handleVideoCpuUsageLevelChange}
|
||||
/>
|
||||
{selectedPresetNote && (
|
||||
<span className="selected-value-note">{selectedPresetNote}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* FRAME RATE FIELD */}
|
||||
<div className="field">
|
||||
{/* VIDEO PASSTHROUGH FIELD */}
|
||||
<div style={{ display: 'none' }} className="form-module">
|
||||
<p className="label">
|
||||
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.framerate.tip} />
|
||||
Frame rate:
|
||||
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.videoPassthrough.tip} />
|
||||
Use Video Passthrough?
|
||||
</p>
|
||||
<div className="form-component">
|
||||
<Slider
|
||||
// tooltipVisible
|
||||
tipFormatter={value => `${value} ${framerateUnit}`}
|
||||
defaultValue={dataState.framerate}
|
||||
value={dataState.framerate}
|
||||
onChange={handleFramerateChange}
|
||||
step={framerateDefaults.incrementBy}
|
||||
min={framerateMin}
|
||||
max={framerateMax}
|
||||
marks={{
|
||||
[framerateMin]: `${framerateMin} ${framerateUnit}`,
|
||||
[framerateMax]: `${framerateMax} ${framerateUnit}`,
|
||||
}}
|
||||
{/* todo: change to ToggleSwitch for layout */}
|
||||
<Switch
|
||||
defaultChecked={dataState.videoPassthrough}
|
||||
checked={dataState.videoPassthrough}
|
||||
onChange={handleVideoPassChange}
|
||||
// label="Use Video Passthrough"
|
||||
checkedChildren="Yes"
|
||||
unCheckedChildren="No"
|
||||
/>
|
||||
{selectedFramerateNote ? (
|
||||
<span className="selected-value-note">{selectedFramerateNote}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
|
||||
{/* VIDEO BITRATE FIELD */}
|
||||
<div className={`form-module ${dataState.videoPassthrough ? 'disabled' : ''}`}>
|
||||
<Typography.Title level={3} className="section-title">
|
||||
Video Bitrate
|
||||
</Typography.Title>
|
||||
<p className="description">{VIDEO_VARIANT_DEFAULTS.videoBitrate.tip}</p>
|
||||
<div className="segment-slider-container">
|
||||
<Slider
|
||||
tipFormatter={value => `${value} ${videoBRUnit}`}
|
||||
disabled={dataState.videoPassthrough === true}
|
||||
defaultValue={dataState.videoBitrate}
|
||||
value={dataState.videoBitrate}
|
||||
onChange={handleVideoBitrateChange}
|
||||
step={videoBitrateDefaults.incrementBy}
|
||||
min={videoBRMin}
|
||||
max={videoBRMax}
|
||||
marks={{
|
||||
[videoBRMin]: `${videoBRMin} ${videoBRUnit}`,
|
||||
[videoBRMax]: `${videoBRMax} ${videoBRUnit}`,
|
||||
}}
|
||||
/>
|
||||
{selectedVideoBRnote && (
|
||||
<span className="selected-value-note">{selectedVideoBRnote}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Collapse className="advanced-settings">
|
||||
<Panel header="Advanced Settings" key="1">
|
||||
<div className="section-intro">
|
||||
Resizing your content will take additional resources on your server. If you wish to
|
||||
optionally resize your output for this stream variant then you should either set the
|
||||
width <strong>or</strong> the height to keep your aspect ratio.
|
||||
</div>
|
||||
<div className="field">
|
||||
<TextField
|
||||
type="number"
|
||||
{...VIDEO_VARIANT_DEFAULTS.scaledWidth}
|
||||
value={dataState.scaledWidth}
|
||||
onChange={handleScaledWidthChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<TextField
|
||||
type="number"
|
||||
{...VIDEO_VARIANT_DEFAULTS.scaledHeight}
|
||||
value={dataState.scaledHeight}
|
||||
onChange={handleScaledHeightChanged}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* FRAME RATE FIELD */}
|
||||
<div className="field">
|
||||
<p className="label">
|
||||
<InfoTip tip={VIDEO_VARIANT_DEFAULTS.framerate.tip} />
|
||||
Frame rate:
|
||||
</p>
|
||||
<div className="segment-slider-container form-component">
|
||||
<Slider
|
||||
// tooltipVisible
|
||||
tipFormatter={value => `${value} ${framerateUnit}`}
|
||||
defaultValue={dataState.framerate}
|
||||
value={dataState.framerate}
|
||||
onChange={handleFramerateChange}
|
||||
step={framerateDefaults.incrementBy}
|
||||
min={framerateMin}
|
||||
max={framerateMax}
|
||||
marks={{
|
||||
[framerateMin]: `${framerateMin} ${framerateUnit}`,
|
||||
[framerateMax]: `${framerateMax} ${framerateUnit}`,
|
||||
}}
|
||||
/>
|
||||
{selectedFramerateNote ? (
|
||||
<span className="selected-value-note">{selectedFramerateNote}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,10 +12,17 @@ import VideoVariantForm from './video-variant-form';
|
|||
import {
|
||||
API_VIDEO_VARIANTS,
|
||||
DEFAULT_VARIANT_STATE,
|
||||
SUCCESS_STATES,
|
||||
RESET_TIMEOUT,
|
||||
postConfigUpdateToAPI,
|
||||
} from '../../utils/config-constants';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_PROCESSING,
|
||||
STATUS_SUCCESS,
|
||||
} from '../../utils/input-statuses';
|
||||
import FormStatusIndicator from './form-status-indicator';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
|
@ -36,8 +43,7 @@ export default function CurrentVariantsTable() {
|
|||
// current data inside modal
|
||||
const [modalDataState, setModalDataState] = useState(DEFAULT_VARIANT_STATE);
|
||||
|
||||
const [submitStatus, setSubmitStatus] = useState(null);
|
||||
const [submitStatusMessage, setSubmitStatusMessage] = useState('');
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
|
@ -52,7 +58,6 @@ export default function CurrentVariantsTable() {
|
|||
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
setSubmitStatusMessage('');
|
||||
resetTimer = null;
|
||||
clearTimeout(resetTimer);
|
||||
};
|
||||
|
@ -65,6 +70,8 @@ export default function CurrentVariantsTable() {
|
|||
|
||||
// posts all the variants at once as an array obj
|
||||
const postUpdateToAPI = async (postValue: any) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath: API_VIDEO_VARIANTS,
|
||||
data: { value: postValue },
|
||||
|
@ -79,8 +86,7 @@ export default function CurrentVariantsTable() {
|
|||
setModalProcessing(false);
|
||||
handleModalCancel();
|
||||
|
||||
setSubmitStatus('success');
|
||||
setSubmitStatusMessage('Variants updated.');
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Variants updated'));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
|
||||
if (serverStatusData.online) {
|
||||
|
@ -90,8 +96,7 @@ export default function CurrentVariantsTable() {
|
|||
}
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus('error');
|
||||
setSubmitStatusMessage(message);
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
});
|
||||
|
@ -112,7 +117,7 @@ export default function CurrentVariantsTable() {
|
|||
postUpdateToAPI(postData);
|
||||
};
|
||||
|
||||
const handleDeleteVariant = index => {
|
||||
const handleDeleteVariant = (index: number) => {
|
||||
const postData = [...videoQualityVariants];
|
||||
postData.splice(index, 1);
|
||||
postUpdateToAPI(postData);
|
||||
|
@ -125,9 +130,6 @@ export default function CurrentVariantsTable() {
|
|||
});
|
||||
};
|
||||
|
||||
const { icon: newStatusIcon = null, message: newStatusMessage = '' } =
|
||||
SUCCESS_STATES[submitStatus] || {};
|
||||
|
||||
const videoQualityColumns: ColumnsType<VideoVariant> = [
|
||||
{
|
||||
title: 'Video bitrate',
|
||||
|
@ -176,12 +178,6 @@ export default function CurrentVariantsTable() {
|
|||
},
|
||||
];
|
||||
|
||||
const statusMessage = (
|
||||
<div className={`status-message ${submitStatus || ''}`}>
|
||||
{newStatusIcon} {newStatusMessage} {submitStatusMessage}
|
||||
</div>
|
||||
);
|
||||
|
||||
const videoQualityVariantData = videoQualityVariants.map((variant, index) => ({
|
||||
key: index + 1,
|
||||
...variant,
|
||||
|
@ -189,9 +185,11 @@ export default function CurrentVariantsTable() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Title level={3}>Stream output</Title>
|
||||
<Title level={3} className="section-title">
|
||||
Stream output
|
||||
</Title>
|
||||
|
||||
{statusMessage}
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
|
||||
<Table
|
||||
className="variants-table"
|
||||
|
@ -207,10 +205,11 @@ export default function CurrentVariantsTable() {
|
|||
onOk={handleModalOk}
|
||||
onCancel={handleModalCancel}
|
||||
confirmLoading={modalProcessing}
|
||||
width={900}
|
||||
>
|
||||
<VideoVariantForm dataState={{ ...modalDataState }} onUpdateField={handleUpdateField} />
|
||||
|
||||
{statusMessage}
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</Modal>
|
||||
<br />
|
||||
<Button
|
||||
|
|
|
@ -49,7 +49,7 @@ export default function MainLayout(props) {
|
|||
const { Header, Footer, Content, Sider } = Layout;
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
const [upgradeVersion, setUpgradeVersion] = useState(null);
|
||||
const [upgradeVersion, setUpgradeVersion] = useState('');
|
||||
const checkForUpgrade = async () => {
|
||||
try {
|
||||
const result = await upgradeVersionAvailable(versionNumber);
|
||||
|
@ -80,7 +80,8 @@ export default function MainLayout(props) {
|
|||
});
|
||||
|
||||
const upgradeMenuItemStyle = upgradeVersion ? 'block' : 'none';
|
||||
const upgradeVersionString = upgradeVersion || '';
|
||||
const upgradeVersionString = `${upgradeVersion}` || '';
|
||||
const upgradeMessage = `Upgrade to v${upgradeVersionString}`;
|
||||
|
||||
const clearAlertMessage = () => {
|
||||
alertMessage.setMessage(null);
|
||||
|
@ -123,10 +124,10 @@ export default function MainLayout(props) {
|
|||
|
||||
<Sider width={240} className="side-nav">
|
||||
<Menu
|
||||
theme="dark"
|
||||
defaultSelectedKeys={[route.substring(1) || 'home']}
|
||||
defaultOpenKeys={['current-stream-menu', 'utilities-menu', 'configuration']}
|
||||
mode="inline"
|
||||
className="menu-container"
|
||||
>
|
||||
<h1 className="owncast-title">
|
||||
<span className="logo-container">
|
||||
|
@ -150,13 +151,6 @@ export default function MainLayout(props) {
|
|||
<Menu.Item key="config-public-details">
|
||||
<Link href="/config-public-details">General</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="config-social-items">
|
||||
<Link href="/config-social-items">Social Links</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item key="config-page-content">
|
||||
<Link href="/config-page-content">Page Content</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item key="config-server-details">
|
||||
<Link href="/config-server-details">Server Setup</Link>
|
||||
|
@ -177,9 +171,7 @@ export default function MainLayout(props) {
|
|||
<Link href="/logs">Logs</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="upgrade" style={{ display: upgradeMenuItemStyle }}>
|
||||
<Link href="/upgrade">
|
||||
<a>Upgrade to v{upgradeVersionString}</a>
|
||||
</Link>
|
||||
<Link href="/upgrade">{upgradeMessage}</Link>
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu key="integrations-menu" icon={<ExperimentOutlined />} title="Integrations">
|
||||
|
|
29
web/package-lock.json
generated
29
web/package-lock.json
generated
|
@ -1587,6 +1587,12 @@
|
|||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/highlight.js": {
|
||||
"version": "9.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz",
|
||||
"integrity": "sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
|
||||
|
@ -1598,6 +1604,23 @@
|
|||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/linkify-it": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.0.tgz",
|
||||
"integrity": "sha512-x9OaQQTb1N2hPZ/LWJsqushexDvz7NgzuZxiRmZio44WPuolTZNHDBCrOxCzRVOMwamJRO2dWax5NbygOf1OTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/markdown-it": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.0.1.tgz",
|
||||
"integrity": "sha512-mHfT8j/XkPb1uLEfs0/C3se6nd+webC2kcqcy8tgcVr0GDEONv/xaQzAN+aQvkxQXk/jC0Q6mPS+0xhFwRF35g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/highlight.js": "^9.7.0",
|
||||
"@types/linkify-it": "*",
|
||||
"@types/mdurl": "*"
|
||||
}
|
||||
},
|
||||
"@types/mdast": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz",
|
||||
|
@ -1606,6 +1629,12 @@
|
|||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"@types/mdurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz",
|
||||
"integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz",
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"devDependencies": {
|
||||
"@types/chart.js": "^2.9.28",
|
||||
"@types/classnames": "^2.2.11",
|
||||
"@types/markdown-it": "^12.0.1",
|
||||
"@types/node": "^14.11.2",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"@types/react": "^16.9.49",
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// order matters!
|
||||
import 'antd/dist/antd.css';
|
||||
import '../styles/colors.scss';
|
||||
import '../styles/globals.scss';
|
||||
import '../styles/ant-overrides.scss';
|
||||
import '../styles/markdown-editor.scss';
|
||||
import '../styles/globals.scss';
|
||||
|
||||
import '../styles/main-layout.scss';
|
||||
|
||||
import '../styles/form-textfields.scss';
|
||||
import '../styles/form-toggleswitch.scss';
|
||||
import '../styles/form-misc-elements.scss';
|
||||
import '../styles/config-socialhandles.scss';
|
||||
import '../styles/config-storage.scss';
|
||||
import '../styles/config-tags.scss';
|
||||
import '../styles/config-video-variants.scss';
|
||||
import '../styles/config-public-details.scss';
|
||||
|
||||
import '../styles/home.scss';
|
||||
import '../styles/chat.scss';
|
||||
|
|
|
@ -1,26 +1,42 @@
|
|||
import React from 'react';
|
||||
import { Typography } from 'antd';
|
||||
import Link from 'next/link';
|
||||
|
||||
import EditInstanceDetails from '../components/config/edit-instance-details';
|
||||
import EditDirectoryDetails from '../components/config/edit-directory';
|
||||
import EditInstanceTags from '../components/config/edit-tags';
|
||||
import EditSocialLinks from '../components/config/edit-social-links';
|
||||
import EditPageContent from '../components/config/edit-page-content';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function PublicFacingDetails() {
|
||||
return (
|
||||
<>
|
||||
<Title level={2}>General Settings</Title>
|
||||
<p>
|
||||
<div className="config-public-details-page">
|
||||
<Title level={2} className="page-title">
|
||||
General Settings
|
||||
</Title>
|
||||
<p className="description">
|
||||
The following are displayed on your site to describe your stream and its content.{' '}
|
||||
<a href="https://owncast.online/docs/website/">Learn more.</a>
|
||||
</p>
|
||||
<div className="edit-public-details-container">
|
||||
<EditInstanceDetails />
|
||||
<EditInstanceTags />
|
||||
<EditDirectoryDetails />
|
||||
|
||||
<div className="top-container">
|
||||
<div className="form-module instance-details-container">
|
||||
<EditInstanceDetails />
|
||||
</div>
|
||||
|
||||
<div className="form-module social-items-container ">
|
||||
<div className="form-module tags-module">
|
||||
<EditInstanceTags />
|
||||
</div>
|
||||
|
||||
<div className="form-module social-handles-container">
|
||||
<EditSocialLinks />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="form-module page-content-module">
|
||||
<EditPageContent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@ const { Title } = Typography;
|
|||
export default function ConfigServerDetails() {
|
||||
return (
|
||||
<div className="config-server-details-form">
|
||||
<Title level={2}>Server Settings</Title>
|
||||
<p>
|
||||
You should change your stream key from the default and keep it safe. For most people it's
|
||||
likely the other settings will not need to be changed.
|
||||
<Title level={2} className="page-title">
|
||||
Server Settings
|
||||
</Title>
|
||||
<p className="description">
|
||||
You should change your stream key from the default and keep it safe. For most people
|
||||
it's likely the other settings will not need to be changed.
|
||||
</p>
|
||||
<div className="config-server-details-container">
|
||||
<div className="form-module config-server-details-container">
|
||||
<EditServerDetails />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,13 +7,15 @@ const { Title } = Typography;
|
|||
export default function ConfigStorageInfo() {
|
||||
return (
|
||||
<>
|
||||
<Title level={2}>Storage</Title>
|
||||
<p>
|
||||
<Title level={2} className="page-title">
|
||||
Storage
|
||||
</Title>
|
||||
<p className="description">
|
||||
Owncast supports optionally using external storage providers to distribute your video. Learn
|
||||
more about this by visiting our{' '}
|
||||
<a href="https://owncast.online/docs/storage/">Storage Documentation</a>.
|
||||
</p>
|
||||
<p>
|
||||
<p className="description">
|
||||
Configuring this incorrectly will likely cause your video to be unplayable. Double check the
|
||||
documentation for your storage provider on how to configure the bucket you created for
|
||||
Owncast.
|
||||
|
|
|
@ -9,22 +9,24 @@ const { Title } = Typography;
|
|||
export default function ConfigVideoSettings() {
|
||||
return (
|
||||
<div className="config-video-variants">
|
||||
<Title level={2}>Video configuration</Title>
|
||||
<p>
|
||||
<Title level={2} className="page-title">
|
||||
Video configuration
|
||||
</Title>
|
||||
<p className="description">
|
||||
Before changing your video configuration{' '}
|
||||
<a href="https://owncast.online/docs/encoding">visit the video documentation</a> to learn
|
||||
how it impacts your stream performance.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<VideoVariantsTable />
|
||||
</p>
|
||||
<br />
|
||||
<hr />
|
||||
<br />
|
||||
<p>
|
||||
<VideoLatency />
|
||||
</p>
|
||||
<div className="row">
|
||||
<div className="form-module variants-table-module">
|
||||
<VideoVariantsTable />
|
||||
</div>
|
||||
|
||||
<div className="form-module latency-module">
|
||||
<VideoLatency />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,52 +1,231 @@
|
|||
// GENERAL ANT OVERRIDES
|
||||
|
||||
|
||||
// RESET BG, TEXT COLORS
|
||||
.ant-layout,
|
||||
.ant-layout-footer,
|
||||
.ant-menu,
|
||||
.ant-menu.ant-menu-dark {
|
||||
background-color: transparent;
|
||||
}
|
||||
.owncast-layout .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background-color: var(--owncast-purple);
|
||||
}
|
||||
|
||||
// LAYOUT
|
||||
.ant-layout-header,
|
||||
.ant-layout-sider {
|
||||
background-color: #07050d;
|
||||
.ant-layout-sider,
|
||||
.ant-layout-footer,
|
||||
.ant-card,
|
||||
.ant-collapse,
|
||||
.ant-collapse-content,
|
||||
.ant-table,
|
||||
.ant-table-thead > tr > th,
|
||||
.ant-table-small .ant-table-thead > tr > th,
|
||||
th.ant-table-column-sort,
|
||||
td.ant-table-column-sort,
|
||||
.ant-table-tbody > tr.ant-table-row:hover > td,
|
||||
.ant-table-thead th.ant-table-column-sort,
|
||||
.ant-menu,
|
||||
.ant-menu-submenu > .ant-menu,
|
||||
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background-color: transparent;
|
||||
color: var(--default-text-color)
|
||||
}
|
||||
|
||||
// MENU
|
||||
.ant-menu-dark .ant-menu-inline.ant-menu-sub {
|
||||
// background-color: rgba(255,255,255,.05);
|
||||
background-color: #140028;
|
||||
|
||||
h1.ant-typography,
|
||||
h2.ant-typography,
|
||||
h3.ant-typography,
|
||||
h4.ant-typography,
|
||||
h5.ant-typography,
|
||||
.ant-typography,
|
||||
.ant-typography h1,
|
||||
.ant-typography h2,
|
||||
.ant-typography h3,
|
||||
.ant-typography h4,
|
||||
.ant-typography h5 {
|
||||
color: var(--default-text-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// CARD
|
||||
.ant-typography.ant-typography-secondary {
|
||||
color: rgba(255,255,255,.85);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.ant-progress-text,
|
||||
.ant-progress-circle .ant-progress-text {
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ANT MENU
|
||||
// menu base
|
||||
.ant-menu-item {
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
|
||||
.anticon {
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
color: var(--nav-text);
|
||||
}
|
||||
|
||||
a {
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
color: var(--nav-text);
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgba(0,0,0,.15);
|
||||
|
||||
.anticon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
// menu item selected
|
||||
.ant-menu-item-selected,
|
||||
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background-color: black;
|
||||
a {
|
||||
color: var(--nav-selected-text);
|
||||
}
|
||||
.anticon {
|
||||
color: var(--nav-selected-text);
|
||||
}
|
||||
|
||||
// the right border color
|
||||
&:after {
|
||||
border-color: var(--nav-selected-text);
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
}
|
||||
}
|
||||
// submenu items
|
||||
.ant-menu-submenu {
|
||||
&> .ant-menu {
|
||||
border-left: 1px solid rgba(255,255,255,.4);
|
||||
background-color: rgba(0,0,0,.15);
|
||||
}
|
||||
.ant-menu-submenu-title {
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
color: var(--nav-text);
|
||||
|
||||
.anticon {
|
||||
color: var(--nav-text);
|
||||
}
|
||||
.ant-menu-submenu-arrow {
|
||||
&:before,
|
||||
&:after {
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
background-image: linear-gradient(to right, var(--nav-text), var(--nav-text));
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.ant-menu-submenu-title {
|
||||
color: white;
|
||||
.anticon {
|
||||
color: white;
|
||||
}
|
||||
.ant-menu-submenu-arrow {
|
||||
&:before,
|
||||
&:after {
|
||||
background-image: linear-gradient(to right, white, white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ANT RESULT
|
||||
.ant-result-title {
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
.ant-result-subtitle {
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
|
||||
|
||||
// ANT CARD
|
||||
.ant-card {
|
||||
border-radius: .5em;
|
||||
border-radius: var(--container-border-radius);
|
||||
background-color: var(--container-bg-color);
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
.ant-card-bordered {
|
||||
border-color: rgba(255,255,255,.25);
|
||||
}
|
||||
.ant-card-meta-title,
|
||||
.ant-card-meta-description {
|
||||
color: white;
|
||||
}
|
||||
|
||||
// INPUT
|
||||
.ant-input-affix-wrapper {
|
||||
// border-radius: 5px;
|
||||
// background-color: rgba(255,255,255,.1);
|
||||
|
||||
|
||||
// ANT INPUT
|
||||
.ant-input-affix-wrapper,
|
||||
.ant-input-number {
|
||||
background-color: var(--textfield-bg);
|
||||
border-color: var(--textfield-border);
|
||||
input,
|
||||
textarea {
|
||||
// border-radius: 5px;
|
||||
background-color: transparent;
|
||||
color: rgba(255,255,255,.85);
|
||||
border-color: rgba(0,0,0,1);
|
||||
&::placeholder {
|
||||
color: var(--textfield-border);
|
||||
}
|
||||
&:-webkit-autofill {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
input {
|
||||
// background-color: transparent;
|
||||
}
|
||||
.ant-input-number:hover,
|
||||
.ant-input-affix-wrapper:hover {
|
||||
border-color: var(--owncast-purple-highlight);
|
||||
input,
|
||||
textarea {
|
||||
border-color: var(--owncast-purple-highlight);
|
||||
}
|
||||
}
|
||||
.ant-input-number:focus,
|
||||
.ant-input-affix-wrapper:focus,
|
||||
.ant-input-affix-wrapper-focused {
|
||||
border-color: var(--owncast-purple);
|
||||
input,
|
||||
textarea {
|
||||
color: white;
|
||||
border-color: var(--owncast-purple);
|
||||
}
|
||||
}
|
||||
.ant-input-textarea-clear-icon,
|
||||
.ant-input-clear-icon {
|
||||
color: rgba(255,255,255,.5);
|
||||
}
|
||||
textarea.ant-input {
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
// BUTTON
|
||||
.ant-btn-primary:hover, .ant-btn-primary:focus {
|
||||
background-color: white;
|
||||
color: #40a9ff;
|
||||
|
||||
|
||||
|
||||
// ANT BUTTON
|
||||
.ant-btn-primary {
|
||||
background-color: var(--owncast-purple);
|
||||
border-color: var(--owncast-purple);
|
||||
}
|
||||
.ant-btn.ant-btn-primary:focus {
|
||||
.ant-btn-primary:hover,
|
||||
.ant-btn-primary:focus {
|
||||
background-color: var(--form-focused);
|
||||
border-color: var(--form-focused);
|
||||
}
|
||||
.ant-btn.ant-btn-primary:hover {
|
||||
border-color: white;
|
||||
}
|
||||
.ant-btn-primary[disabled] {
|
||||
background-color: rgba(255,255,255,.2);
|
||||
border-color: rgba(255,255,255,.2);
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255,255,255,.2);
|
||||
border-color: rgba(255,255,255,.2);
|
||||
}
|
||||
}
|
||||
.ant-input-affix-wrapper,
|
||||
.ant-btn {
|
||||
|
@ -54,29 +233,72 @@
|
|||
transition-duration: 0.15s;
|
||||
}
|
||||
|
||||
// TABLE
|
||||
// ANT TABLE
|
||||
.ant-table-thead > tr > th,
|
||||
.ant-table-small .ant-table-thead > tr > th {
|
||||
background-color: #000;
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
background-color: #112;
|
||||
font-weight: 500;
|
||||
color: var(--owncast-purple);
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr > td,
|
||||
.ant-table-thead > tr > th,
|
||||
.ant-table-small .ant-table-thead > tr > th {
|
||||
border-color: var(--textfield-border);
|
||||
}
|
||||
.ant-table-tbody > tr > td {
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
background-color: var(--textfield-bg);
|
||||
}
|
||||
.ant-table-tbody > tr:nth-child(odd) > td {
|
||||
background-color: var(--textfield-bg);
|
||||
}
|
||||
.ant-empty {
|
||||
color: white;
|
||||
opacity: .75;
|
||||
}
|
||||
.ant-table-empty .ant-table-tbody > tr.ant-table-placeholder {
|
||||
&:hover > td {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.ant-table-thead th.ant-table-column-has-sorters:hover {
|
||||
background-color: var(--textfield-border);
|
||||
.ant-table-filter-trigger-container {
|
||||
background-color: var(--textfield-border);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MODAL
|
||||
.ant-modal-content {
|
||||
border-radius: 6px;
|
||||
border-radius: var(--container-border-radius);
|
||||
border: 1px solid var(--owncast-purple-highlight);
|
||||
}
|
||||
.ant-modal-header {
|
||||
background-color: #1c173d;
|
||||
border-radius: 6px 6px 0 0;
|
||||
border-radius: var(--container-border-radius) var(--container-border-radius) 0 0;
|
||||
}
|
||||
.ant-modal-close-x {
|
||||
color: white;
|
||||
}
|
||||
.ant-modal-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.5em;
|
||||
font-weight: 500;
|
||||
font-size: 1.25em;
|
||||
color: var(--nav-selected-text);
|
||||
}
|
||||
.ant-modal-body {
|
||||
background-color: #33333c;
|
||||
background-color: var(--nav-bg-color);
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
.ant-modal-header,
|
||||
.ant-modal-footer {
|
||||
background-color: #222229;
|
||||
background-color: black;
|
||||
}
|
||||
.ant-modal-content,
|
||||
.ant-modal-header,
|
||||
.ant-modal-footer {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
// SELECT
|
||||
|
@ -86,10 +308,59 @@
|
|||
|
||||
|
||||
// SLIDER
|
||||
.ant-slider-with-marks {
|
||||
margin-right: 2em;
|
||||
}
|
||||
// .ant-slider-with-marks {
|
||||
// margin-right: 2em;
|
||||
// }
|
||||
.ant-slider-mark-text {
|
||||
font-size: .85em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// ANT SWITCH
|
||||
.ant-switch {
|
||||
background-color: #666;
|
||||
}
|
||||
.ant-switch-checked {
|
||||
background-color: var(--ant-success);
|
||||
.ant-switch-inner {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ANT COLLAPSE
|
||||
.ant-collapse {
|
||||
border-color: transparent;
|
||||
&> .ant-collapse-item,
|
||||
.ant-collapse-content {
|
||||
border-color: transparent;
|
||||
&> .ant-collapse-header {
|
||||
border-color: transparent;
|
||||
background-color: var(--textfield-bg);
|
||||
color: var(--nav-text);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-collapse-content {
|
||||
background-color: #181231;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ANT POPOVER
|
||||
.ant-popover {
|
||||
|
||||
}
|
||||
.ant-popover-inner {
|
||||
background-color: black;
|
||||
}
|
||||
.ant-popover-message,
|
||||
.ant-popover-inner-content {
|
||||
color: var(--default-text-color);
|
||||
|
||||
}
|
||||
.ant-popover-placement-topLeft > .ant-popover-content > .ant-popover-arrow {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,35 @@
|
|||
// rename to variables.scss
|
||||
|
||||
:root {
|
||||
--owncast-purple: rgba(90,103,216,1);
|
||||
|
||||
--default-text-color: #fff;
|
||||
|
||||
--owncast-purple: rgba(90,103,216,1); //5a67d8
|
||||
--owncast-purple-highlight: #ccd;
|
||||
|
||||
--online-color: #73dd3f;
|
||||
|
||||
--owncast-dark1: #1f1f21;
|
||||
|
||||
|
||||
--ant-error: #ff4d4f;
|
||||
--ant-success: #52c41a;
|
||||
--ant-warning: #faad14;
|
||||
--ant-transition-duration: .15s;
|
||||
|
||||
|
||||
--container-bg-color: #1A1C24;
|
||||
--container-bg-color-alt: #251c49;
|
||||
--container-border-radius: 2px;
|
||||
|
||||
--code-purple: #82aaff;
|
||||
|
||||
--nav-bg-color: #1A1C24;
|
||||
--nav-text: #6a76ba;
|
||||
--nav-selected-text: #c48dff;
|
||||
|
||||
--form-focused: #8d71ff;
|
||||
|
||||
--textfield-border: #373640;
|
||||
--textfield-bg: #100f0f;
|
||||
|
||||
}
|
||||
|
|
65
web/styles/config-public-details.scss
Normal file
65
web/styles/config-public-details.scss
Normal file
|
@ -0,0 +1,65 @@
|
|||
// dealiing with some general layout on the public details page
|
||||
|
||||
.config-public-details-page {
|
||||
width: 100%;
|
||||
.top-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
flex-wrap: wrap;
|
||||
.social-items-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
flex-wrap: nowrap;
|
||||
margin: 1em 0;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
.tags-module {
|
||||
margin-right: 1em;
|
||||
}
|
||||
.form-module {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
flex-direction: column;
|
||||
.form-module {
|
||||
width: 100%;
|
||||
}
|
||||
.tags-module {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.instance-details-container {
|
||||
width: 100%;
|
||||
}
|
||||
.social-items-container {
|
||||
background-color: var(--container-bg-color-alt);
|
||||
padding: 0 .75em;
|
||||
margin-left: 1em;
|
||||
max-width: 450px;
|
||||
.form-module {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.social-handles-container {
|
||||
min-width: 350px;
|
||||
}
|
||||
}
|
||||
.instance-details-container,
|
||||
.page-content-module {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.field-summary {
|
||||
textarea {
|
||||
height: 6em !important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,3 +23,35 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.social-links-edit-container {
|
||||
.social-handles-table {
|
||||
.social-handle-cell {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
color: rgba(255,255,255,.85);
|
||||
|
||||
.option-icon {
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
line-height: normal;
|
||||
}
|
||||
.option-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 0 0 1em;
|
||||
line-height: 2;
|
||||
font-size: .85em;
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
width: 6em;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
|
||||
.edit-storage-container {
|
||||
padding: 1em;
|
||||
.form-fields {
|
||||
display: none;
|
||||
margin-bottom: 1em;
|
||||
|
@ -13,7 +14,7 @@
|
|||
}
|
||||
|
||||
.button-container {
|
||||
margin: 1em 0;
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
.advanced-section {
|
||||
margin: 1em 0;
|
||||
|
|
|
@ -6,19 +6,24 @@
|
|||
font-size: .85rem;
|
||||
border-radius: 10em;
|
||||
padding: .25em 1em;
|
||||
background-color: rgba(255,255,255,.5);
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ant-tag-close-icon {
|
||||
transform: translateY(-1px);
|
||||
margin-left: .3rem;
|
||||
padding: 2px;
|
||||
border-radius: 5rem;
|
||||
border: 1px solid #eee;
|
||||
color: black;
|
||||
border: 1px solid #000;
|
||||
transition-duration: var(--ant-transition-duration);
|
||||
&:hover {
|
||||
border-color: #e03;
|
||||
border-color: #5a67d8;
|
||||
background-color: white;
|
||||
svg {
|
||||
fill: black;
|
||||
transition: fill .3s;
|
||||
transition: fill var(--ant-transition-duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +35,5 @@
|
|||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.new-tag-input {
|
||||
width: 16em;
|
||||
}
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,30 @@
|
|||
// styles for Video variant editor (table + modal)
|
||||
|
||||
.config-video-variants {
|
||||
|
||||
.variants-table {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.variants-table-module {
|
||||
min-width: 48%;
|
||||
max-width: 600px;
|
||||
margin-right: 1em
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// modal content
|
||||
.config-variant-form {
|
||||
.description {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
.advanced-settings {
|
||||
width: 48%;
|
||||
margin-left: 2em;
|
||||
}
|
||||
.blurb {
|
||||
margin: 1em;
|
||||
opacity: .75;
|
||||
|
@ -13,58 +36,61 @@
|
|||
opacity: .5;
|
||||
font-style: italic;
|
||||
}
|
||||
.section-intro {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 2em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
transform: opacity .15s;
|
||||
&.disabled {
|
||||
opacity: .25;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 40%;
|
||||
text-align: right;
|
||||
padding-right: 2em;
|
||||
font-weight: bold;
|
||||
color: var(--owncast-purple);
|
||||
}
|
||||
.info-tip {
|
||||
margin-right: 1em;
|
||||
}
|
||||
.form-component {
|
||||
width: 60%;
|
||||
|
||||
// .field {
|
||||
// margin-bottom: 2em;
|
||||
// display: flex;
|
||||
// flex-direction: row;
|
||||
// justify-content: center;
|
||||
// align-items: flex-start;
|
||||
// transform: opacity .15s;
|
||||
// &.disabled {
|
||||
// opacity: .25;
|
||||
// }
|
||||
|
||||
// .label {
|
||||
// width: 40%;
|
||||
// text-align: right;
|
||||
// padding-right: 2em;
|
||||
// font-weight: bold;
|
||||
// color: var(--owncast-purple);
|
||||
// }
|
||||
// .info-tip {
|
||||
// margin-right: 1em;
|
||||
// }
|
||||
// .form-component {
|
||||
// width: 60%;
|
||||
|
||||
.selected-value-note {
|
||||
font-size: .85em;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-collapse {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.ant-collapse > .ant-collapse-item:last-child,
|
||||
.ant-collapse > .ant-collapse-item:last-child > .ant-collapse-header {
|
||||
border: none;
|
||||
background-color: rgba(0,0,0,.25);
|
||||
border-radius: 6px;
|
||||
}
|
||||
.ant-collapse-content {
|
||||
background-color: rgba(0,0,0,.1);
|
||||
}
|
||||
// .selected-value-note {
|
||||
// font-size: .85em;
|
||||
// display: inline-block;
|
||||
// text-align: center;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .ant-collapse {
|
||||
// border: none;
|
||||
// border-radius: 6px;
|
||||
// }
|
||||
// .ant-collapse > .ant-collapse-item:last-child,
|
||||
// .ant-collapse > .ant-collapse-item:last-child > .ant-collapse-header {
|
||||
// border: none;
|
||||
// background-color: rgba(0,0,0,.25);
|
||||
// border-radius: 6px;
|
||||
// }
|
||||
// .ant-collapse-content {
|
||||
// background-color: rgba(0,0,0,.1);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
.config-video-segements-conatiner {
|
||||
// display: flex;
|
||||
// flex-direction: row;
|
||||
// justify-content: center;
|
||||
// align-items: flex-start;
|
||||
|
||||
.status-message {
|
||||
text-align: center;
|
||||
|
@ -82,4 +108,8 @@
|
|||
margin-left: .5em;
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings {
|
||||
margin-top: 2em;
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
|
||||
// todo: put these somewhere else
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.config-page-content-form {
|
||||
.edit-page-content {
|
||||
.page-content-actions {
|
||||
margin-top: 1em;
|
||||
display: flex;
|
||||
|
@ -26,25 +27,3 @@
|
|||
margin: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// .social-option {
|
||||
// .ant-select-item-option-content {
|
||||
// display: flex;
|
||||
// flex-direction: row;
|
||||
// justify-content: flex-start;
|
||||
// align-items: center;
|
||||
// padding: .25em;
|
||||
|
||||
// .option-icon {
|
||||
// height: 1.75em;
|
||||
// width: 1.75em;
|
||||
// }
|
||||
// .option-label {
|
||||
// display: inline-block;
|
||||
// margin-left: 1em;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
/* TIP CONTAINER BASE */
|
||||
.field-tip {
|
||||
font-size: .7em;
|
||||
font-size: .8em;
|
||||
color: rgba(255,255,255,.5)
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,9 @@ Ideal for wrapping each Textfield on a page with many text fields in a row. This
|
|||
*/
|
||||
.field-container {
|
||||
padding: .85em 0 .5em;
|
||||
&:nth-child(even) {
|
||||
background-color: rgba(0,0,0,.25);
|
||||
}
|
||||
// &:nth-child(even) {
|
||||
// background-color: rgba(0,0,0,.25);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
@ -50,8 +50,27 @@ Ideal for wrapping each Textfield on a page with many text fields in a row. This
|
|||
width: 90%;
|
||||
margin: auto;
|
||||
padding: 1em 2em .75em;
|
||||
background-color: black;
|
||||
background-color: var(--textfield-border);
|
||||
border-radius: 1em;
|
||||
.ant-slider-rail {
|
||||
background-color: black;
|
||||
}
|
||||
.ant-slider-track {
|
||||
background-color: var(--nav-text);
|
||||
}
|
||||
.ant-slider-mark-text,
|
||||
.ant-slider-mark-text-active {
|
||||
color: white;
|
||||
opacity: .5;
|
||||
}
|
||||
.ant-slider-mark-text-active {
|
||||
opacity: 1;
|
||||
}
|
||||
.status-container {
|
||||
width: 100%;
|
||||
margin: .5em auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
// Base styles for form-textfield, form-textfield-with-submit
|
||||
/*
|
||||
Base styles for
|
||||
- form-textfield,
|
||||
- form-textfield-with-submit
|
||||
- form-toggleswitch
|
||||
|
||||
Both text and toggle use this class for base layout.
|
||||
*/
|
||||
.formfield-container {
|
||||
--form-label-container-width: 15em;
|
||||
|
||||
/* TEXTFIELD-CONTAINER BASE */
|
||||
.textfield-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
|
@ -10,14 +16,14 @@
|
|||
width: 100%;
|
||||
max-width: 600px;
|
||||
.label-side {
|
||||
padding-right: .75em;
|
||||
padding-right: 1.25em;
|
||||
text-align: right;
|
||||
width: 12em;
|
||||
width: var(--form-label-container-width);
|
||||
margin: .2em 0;
|
||||
}
|
||||
.textfield-label {
|
||||
font-weight: 400;
|
||||
font-size: .85em;
|
||||
.formfield-label {
|
||||
font-weight: 500;
|
||||
font-size: 1em;
|
||||
color: var(--owncast-purple);
|
||||
|
||||
&::after {
|
||||
|
@ -25,7 +31,7 @@
|
|||
}
|
||||
}
|
||||
&.required {
|
||||
.textfield-label {
|
||||
.formfield-label {
|
||||
&::before {
|
||||
content: '*';
|
||||
display: inline-block;
|
||||
|
@ -97,7 +103,7 @@
|
|||
justify-content: flex-start;
|
||||
|
||||
.label-spacer {
|
||||
width: 12em;
|
||||
width: var(--form-label-container-width);
|
||||
}
|
||||
.lower-content {
|
||||
display: flex;
|
||||
|
@ -138,3 +144,26 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* TOGGLE SWITCH CONTAINER BASE */
|
||||
.toggleswitch-container {
|
||||
margin: 2em 0 1em;
|
||||
|
||||
.label-side {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.status-container {
|
||||
width: auto;
|
||||
margin: 0 0 0 1em;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/* TOGGLE SWITCH-WITH-SUBMIT-CONTAINER BASE */
|
||||
|
||||
|
||||
.toggleswitch-container {
|
||||
|
||||
.status-container {
|
||||
margin-top: .25rem;
|
||||
}
|
||||
|
||||
.toggleswitch {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
.label {
|
||||
font-weight: bold;
|
||||
color: var(--owncast-purple);
|
||||
}
|
||||
.info-tip {
|
||||
margin-left: .5rem;
|
||||
svg {
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
.ant-form-item {
|
||||
margin: 0 .75rem 0 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@import "~antd/dist/antd.dark";
|
||||
// @import "~antd/dist/antd.dark";
|
||||
|
||||
html,
|
||||
body {
|
||||
|
@ -6,15 +6,20 @@ body {
|
|||
margin: 0;
|
||||
font-family: system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
|
||||
background-color: #1f1f21;
|
||||
background-color: #000;
|
||||
color: var(--default-text-color);;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
color: rgba(90,103,216,1);
|
||||
color: var(--owncast-purple);
|
||||
|
||||
&:hover {
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
|
@ -40,13 +45,50 @@ code {
|
|||
height: 2rem;
|
||||
width: 2rem;
|
||||
}
|
||||
.ant-btn {
|
||||
transition-duration: .15s;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
p.page-description {
|
||||
p.description {
|
||||
margin: 1em 0;
|
||||
color: #ccc;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
|
||||
.line-chart-container {
|
||||
margin: 2em auto;
|
||||
}
|
||||
|
||||
h2.ant-typography.page-title,
|
||||
h3.ant-typography.page-title
|
||||
{
|
||||
font-weight: 400;
|
||||
font-size: 1.5em;
|
||||
color: var(--nav-selected-text);
|
||||
}
|
||||
h2.section-title,
|
||||
h3.section-title {
|
||||
font-weight: 400;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.form-module {
|
||||
// width: 100%;
|
||||
// max-width: 500px;
|
||||
// min-width: 300px;
|
||||
margin: 1em 0;
|
||||
background-color: var(--container-bg-color);
|
||||
padding: 2em;
|
||||
border-radius: var(--container-border-radius);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
@media (max-width: 980px) {
|
||||
flex-direction: column;
|
||||
.form-module {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
height: 100vh;
|
||||
overflow: auto;
|
||||
z-index: 10;
|
||||
background-color: var(--nav-bg-color);
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
h1.owncast-title {
|
||||
|
@ -40,6 +45,7 @@
|
|||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding-right: 1rem;
|
||||
background-color: var(--nav-bg-color);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,17 +5,24 @@
|
|||
display: block !important;
|
||||
}
|
||||
.rc-md-editor {
|
||||
border-color: black !important;
|
||||
border: 1px solid black;
|
||||
background-color: black !important;
|
||||
.rc-md-navigation {
|
||||
background-color: black;
|
||||
border-color: black;
|
||||
}
|
||||
// Set the background color of the preview container
|
||||
.editor-container {
|
||||
background-color: #E2E8F0;
|
||||
color: rgba(45,55,72,1);
|
||||
background-color: rgba(226,232,240, 1) !important;
|
||||
|
||||
}
|
||||
|
||||
// Custom CSS for formatting the preview text
|
||||
.markdown-editor-preview-pane {
|
||||
// color:lightgrey;
|
||||
a {
|
||||
color: var(--owncast-purple);;
|
||||
color: var(--owncast-purple);
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
|
@ -24,20 +31,20 @@
|
|||
|
||||
// Custom CSS class used to format the text of the editor
|
||||
.markdown-editor-pane {
|
||||
color: white !important;
|
||||
color: rgba(255,255,255,.85) !important;
|
||||
border-color: black !important;
|
||||
background-color: black;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Set the background color of the editor text input
|
||||
textarea {
|
||||
background-color: rgb(44,44,44) !important;
|
||||
color:lightgrey !important;
|
||||
background-color: #223 !important;
|
||||
color: rgba(255,255,255,.5) !important;
|
||||
}
|
||||
.ant-btn {
|
||||
transition-duration: .15s;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
|
||||
// Hide extra toolbar buttons.
|
||||
.button-type-undo, .button-type-redo, .button-type-clear, .button-type-image, .button-type-wrap, .button-type-quote, .button-type-strikethrough, .button-type-code-inline, .button-type-code-block {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
// DEFAULT VALUES
|
||||
import React from 'react';
|
||||
import { CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons';
|
||||
import { fetchData, SERVER_CONFIG_UPDATE_URL } from './apis';
|
||||
import { ApiPostArgs, VideoVariant, SocialHandle } from '../types/config-section';
|
||||
|
||||
|
@ -8,17 +6,6 @@ export const TEXT_MAXLENGTH = 255;
|
|||
|
||||
export const RESET_TIMEOUT = 3000;
|
||||
|
||||
export const SUCCESS_STATES = {
|
||||
success: {
|
||||
icon: <CheckCircleFilled style={{ color: 'green' }} />,
|
||||
message: 'Success!',
|
||||
},
|
||||
error: {
|
||||
icon: <ExclamationCircleFilled style={{ color: 'red' }} />,
|
||||
message: 'An error occurred.',
|
||||
},
|
||||
};
|
||||
|
||||
// CONFIG API ENDPOINTS
|
||||
export const API_CUSTOM_CONTENT = '/pagecontent';
|
||||
export const API_FFMPEG = '/ffmpegpath';
|
||||
|
|
Loading…
Reference in a new issue