Merge branch 'webv2' into fix/ImplementPasswordRules
This commit is contained in:
118
web/components/admin/EditCustomJavascript.tsx
Normal file
118
web/components/admin/EditCustomJavascript.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React, { useState, useEffect, useContext, FC } from 'react';
|
||||
import { Typography, Button } from 'antd';
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { bbedit } from '@uiw/codemirror-theme-bbedit';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
|
||||
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||
import {
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
API_CUSTOM_JAVASCRIPT,
|
||||
} from '../../utils/config-constants';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_PROCESSING,
|
||||
STATUS_SUCCESS,
|
||||
} from '../../utils/input-statuses';
|
||||
import { FormStatusIndicator } from './FormStatusIndicator';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const EditCustomJavascript: FC = () => {
|
||||
const [content, setContent] = useState('/* Enter custom Javascript here */');
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
const [hasChanged, setHasChanged] = useState(false);
|
||||
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
|
||||
const { instanceDetails } = serverConfig;
|
||||
const { customJavascript: initialContent } = instanceDetails;
|
||||
|
||||
let resetTimer = null;
|
||||
|
||||
// Clear out any validation states and messaging
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
setHasChanged(false);
|
||||
clearTimeout(resetTimer);
|
||||
resetTimer = null;
|
||||
};
|
||||
|
||||
// posts all the tags at once as an array obj
|
||||
async function handleSave() {
|
||||
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath: API_CUSTOM_JAVASCRIPT,
|
||||
data: { value: content },
|
||||
onSuccess: (message: string) => {
|
||||
setFieldInConfigState({
|
||||
fieldName: 'customJavascript',
|
||||
value: content,
|
||||
path: 'instanceDetails',
|
||||
});
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, message));
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
},
|
||||
});
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setContent(initialContent);
|
||||
}, [instanceDetails]);
|
||||
|
||||
const onCSSValueChange = React.useCallback(value => {
|
||||
setContent(value);
|
||||
if (value !== initialContent && !hasChanged) {
|
||||
setHasChanged(true);
|
||||
} else if (value === initialContent && hasChanged) {
|
||||
setHasChanged(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="edit-custom-css">
|
||||
<Title level={3} className="section-title">
|
||||
Customize your page styling with CSS
|
||||
</Title>
|
||||
|
||||
<p className="description">
|
||||
Customize the look and feel of your Owncast instance by overriding the CSS styles of various
|
||||
components on the page. Refer to the{' '}
|
||||
<a href="https://owncast.online/docs/website/" rel="noopener noreferrer" target="_blank">
|
||||
CSS & Components guide
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p className="description">
|
||||
Please input plain CSS text, as this will be directly injected onto your page during load.
|
||||
</p>
|
||||
|
||||
<CodeMirror
|
||||
value={content}
|
||||
placeholder="/* Enter custom Javascript here */"
|
||||
theme={bbedit}
|
||||
height="200px"
|
||||
extensions={[javascript()]}
|
||||
onChange={onCSSValueChange}
|
||||
/>
|
||||
|
||||
<br />
|
||||
<div className="page-content-actions">
|
||||
{hasChanged && (
|
||||
<Button type="primary" onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -261,7 +261,7 @@ export const MainLayout: FC<MainLayoutProps> = ({ children }) => {
|
||||
},
|
||||
upgradeVersion && {
|
||||
key: 'upgrade',
|
||||
label: <Link href="/upgrade">{upgradeMessage}</Link>,
|
||||
label: <Link href="/admin/upgrade">{upgradeMessage}</Link>,
|
||||
},
|
||||
{
|
||||
key: 'help',
|
||||
|
||||
@@ -125,7 +125,7 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
||||
|
||||
if (!config?.federation?.enabled) {
|
||||
data.push({
|
||||
icon: <img alt="fediverse" width="20px" src="fediverse-white.png" />,
|
||||
icon: <img alt="fediverse" width="20px" src="/img/fediverse-color.png" />,
|
||||
title: 'Add your Owncast instance to the Fediverse',
|
||||
content: (
|
||||
<div>
|
||||
|
||||
@@ -284,6 +284,8 @@ export const VideoVariantForm: FC<VideoVariantFormProps> = ({
|
||||
onConfirm={handleVideoPassConfirm}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
getPopupContainer={triggerNode => triggerNode}
|
||||
placement="topLeft"
|
||||
>
|
||||
{/* adding an <a> tag to force Popcofirm to register click on toggle */}
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import React, { FC, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { Button, Col, Collapse, Row, Slider, Space } from 'antd';
|
||||
import Paragraph from 'antd/lib/typography/Paragraph';
|
||||
@@ -35,49 +35,39 @@ const chatColorVariables = [
|
||||
{ name: 'theme-color-users-7', description: '' },
|
||||
];
|
||||
|
||||
const paletteVariables = [
|
||||
{ name: 'theme-color-palette-0', description: '' },
|
||||
{ name: 'theme-color-palette-1', description: '' },
|
||||
{ name: 'theme-color-palette-2', description: '' },
|
||||
{ name: 'theme-color-palette-3', description: '' },
|
||||
{ name: 'theme-color-palette-4', description: '' },
|
||||
{ name: 'theme-color-palette-5', description: '' },
|
||||
{ name: 'theme-color-palette-6', description: '' },
|
||||
{ name: 'theme-color-palette-7', description: '' },
|
||||
{ name: 'theme-color-palette-8', description: '' },
|
||||
{ name: 'theme-color-palette-9', description: '' },
|
||||
{ name: 'theme-color-palette-10', description: '' },
|
||||
{ name: 'theme-color-palette-11', description: '' },
|
||||
{ name: 'theme-color-palette-12', description: '' },
|
||||
];
|
||||
|
||||
const componentColorVariables = [
|
||||
{ name: 'theme-color-background-main', description: 'Background' },
|
||||
{ name: 'theme-color-action', description: 'Action' },
|
||||
{ name: 'theme-color-action-hover', description: 'Action Hover' },
|
||||
{ name: 'theme-color-components-primary-button-border', description: 'Primary Button Border' },
|
||||
{ name: 'theme-color-components-primary-button-text', description: 'Primary Button Text' },
|
||||
{ name: 'theme-color-components-chat-background', description: 'Chat Background' },
|
||||
{ name: 'theme-color-components-chat-text', description: 'Text: Chat' },
|
||||
{ name: 'theme-color-components-text-on-dark', description: 'Text: Light' },
|
||||
{ name: 'theme-color-components-text-on-light', description: 'Text: Dark' },
|
||||
{ name: 'theme-color-background-header', description: 'Header/Footer' },
|
||||
{ name: 'theme-color-components-content-background', description: 'Page Content' },
|
||||
{ name: 'theme-color-components-scrollbar-background', description: 'Scrollbar Background' },
|
||||
{ name: 'theme-color-components-scrollbar-thumb', description: 'Scrollbar Thumb' },
|
||||
{
|
||||
name: 'theme-color-components-video-status-bar-background',
|
||||
description: 'Video Status Bar Background',
|
||||
},
|
||||
{
|
||||
name: 'theme-color-components-video-status-bar-foreground',
|
||||
description: 'Video Status Bar Foreground',
|
||||
},
|
||||
];
|
||||
|
||||
const others = [{ name: 'theme-rounded-corners', description: 'Corner radius' }];
|
||||
|
||||
// Create an object so these vars can be indexed by name.
|
||||
const allAvailableValues = [
|
||||
...paletteVariables,
|
||||
...componentColorVariables,
|
||||
...chatColorVariables,
|
||||
...others,
|
||||
].reduce((obj, val) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
obj[val.name] = { name: val.name, description: val.description };
|
||||
return obj;
|
||||
}, {});
|
||||
const allAvailableValues = [...componentColorVariables, ...chatColorVariables, ...others].reduce(
|
||||
(obj, val) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
obj[val.name] = { name: val.name, description: val.description };
|
||||
return obj;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
function ColorPicker({
|
||||
@@ -106,6 +96,7 @@ function ColorPicker({
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function Appearance() {
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
@@ -113,7 +104,9 @@ export default function Appearance() {
|
||||
const { instanceDetails } = serverConfig;
|
||||
const { appearanceVariables } = instanceDetails;
|
||||
|
||||
const [colors, setColors] = useState<Record<string, AppearanceVariable>>();
|
||||
const [defaultValues, setDefaultValues] = useState<Record<string, AppearanceVariable>>();
|
||||
const [customValues, setCustomValues] = useState<Record<string, AppearanceVariable>>();
|
||||
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
let resetTimer = null;
|
||||
@@ -123,39 +116,37 @@ export default function Appearance() {
|
||||
clearTimeout(resetTimer);
|
||||
};
|
||||
|
||||
const setColorDefaults = () => {
|
||||
const setDefaults = () => {
|
||||
const c = {};
|
||||
[...paletteVariables, ...componentColorVariables, ...chatColorVariables, ...others].forEach(
|
||||
color => {
|
||||
const resolvedColor = getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--${color.name}`,
|
||||
);
|
||||
c[color.name] = { value: resolvedColor.trim(), description: color.description };
|
||||
},
|
||||
);
|
||||
setColors(c);
|
||||
[...componentColorVariables, ...chatColorVariables, ...others].forEach(color => {
|
||||
const resolvedColor = getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--${color.name}`,
|
||||
);
|
||||
c[color.name] = { value: resolvedColor.trim(), description: color.description };
|
||||
});
|
||||
setDefaultValues(c);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setColorDefaults();
|
||||
setDefaults();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(appearanceVariables).length === 0) return;
|
||||
|
||||
const c = colors || {};
|
||||
const c = {};
|
||||
Object.keys(appearanceVariables).forEach(key => {
|
||||
c[key] = {
|
||||
value: appearanceVariables[key],
|
||||
description: allAvailableValues[key]?.description || '',
|
||||
};
|
||||
});
|
||||
setColors(c);
|
||||
setCustomValues(c);
|
||||
}, [appearanceVariables]);
|
||||
|
||||
const updateColor = (variable: string, color: string, description: string) => {
|
||||
setColors({
|
||||
...colors,
|
||||
setCustomValues({
|
||||
...customValues,
|
||||
[variable]: { value: color, description },
|
||||
});
|
||||
};
|
||||
@@ -167,7 +158,7 @@ export default function Appearance() {
|
||||
onSuccess: () => {
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
setColorDefaults();
|
||||
setCustomValues(null);
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
@@ -178,8 +169,8 @@ export default function Appearance() {
|
||||
|
||||
const save = async () => {
|
||||
const c = {};
|
||||
Object.keys(colors).forEach(color => {
|
||||
c[color] = colors[color].value;
|
||||
Object.keys(customValues).forEach(color => {
|
||||
c[color] = customValues[color].value;
|
||||
});
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
@@ -202,7 +193,31 @@ export default function Appearance() {
|
||||
updateColor(variableName, `${value.toString()}px`, '');
|
||||
};
|
||||
|
||||
if (!colors) {
|
||||
type ColorCollectionProps = {
|
||||
variables: { name; description }[];
|
||||
};
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
const ColorCollection: FC<ColorCollectionProps> = ({ variables }) => {
|
||||
const cc = variables.map(colorVar => {
|
||||
const source = customValues?.[colorVar.name] ? customValues : defaultValues;
|
||||
const { name, description } = colorVar;
|
||||
const { value } = source[name];
|
||||
|
||||
return (
|
||||
<ColorPicker
|
||||
key={name}
|
||||
value={value}
|
||||
name={name}
|
||||
description={description}
|
||||
onChange={updateColor}
|
||||
/>
|
||||
);
|
||||
});
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <>{cc}</>;
|
||||
};
|
||||
|
||||
if (!defaultValues) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
@@ -217,56 +232,15 @@ export default function Appearance() {
|
||||
Certain sections of the interface can be customized by selecting new colors for them.
|
||||
</p>
|
||||
<Row gutter={[16, 16]}>
|
||||
{componentColorVariables.map(colorVar => {
|
||||
const { name } = colorVar;
|
||||
const c = colors[name];
|
||||
|
||||
return (
|
||||
<ColorPicker
|
||||
key={name}
|
||||
value={c.value}
|
||||
name={name}
|
||||
description={c.description}
|
||||
onChange={updateColor}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ColorCollection variables={componentColorVariables} />
|
||||
</Row>
|
||||
</Panel>
|
||||
<Panel header={<Title level={3}>Chat User Colors</Title>} key="2">
|
||||
<Row gutter={[16, 16]}>
|
||||
{chatColorVariables.map(colorVar => {
|
||||
const { name } = colorVar;
|
||||
const c = colors[name];
|
||||
return (
|
||||
<ColorPicker
|
||||
key={name}
|
||||
value={c.value}
|
||||
name={name}
|
||||
description={c.description}
|
||||
onChange={updateColor}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Panel>
|
||||
<Panel header={<Title level={3}>Theme Colors</Title>} key="3">
|
||||
<Row gutter={[16, 16]}>
|
||||
{paletteVariables.map(colorVar => {
|
||||
const { name } = colorVar;
|
||||
const c = colors[name];
|
||||
return (
|
||||
<ColorPicker
|
||||
key={name}
|
||||
value={c.value}
|
||||
name={name}
|
||||
description={c.description}
|
||||
onChange={updateColor}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ColorCollection variables={chatColorVariables} />
|
||||
</Row>
|
||||
</Panel>
|
||||
|
||||
<Panel header={<Title level={3}>Other Settings</Title>} key="4">
|
||||
How rounded should corners be?
|
||||
<Row gutter={[16, 16]}>
|
||||
@@ -278,7 +252,9 @@ export default function Appearance() {
|
||||
onChange={v => {
|
||||
onBorderRadiusChange(v);
|
||||
}}
|
||||
value={Number(colors['theme-rounded-corners']?.value?.replace('px', '') || 0)}
|
||||
value={Number(
|
||||
defaultValues['theme-rounded-corners']?.value?.replace('px', '') || 0,
|
||||
)}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
@@ -286,7 +262,7 @@ export default function Appearance() {
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '30px',
|
||||
borderRadius: `${colors['theme-rounded-corners']?.value}`,
|
||||
borderRadius: `${defaultValues['theme-rounded-corners']?.value}`,
|
||||
backgroundColor: 'var(--theme-color-palette-7)',
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
import { Button, Typography } from 'antd';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
||||
import { TextField, TEXTFIELD_TYPE_PASSWORD } from '../TextField';
|
||||
import { FormStatusIndicator } from '../FormStatusIndicator';
|
||||
import {
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
TWITTER_CONFIG_FIELDS,
|
||||
} from '../../../utils/config-constants';
|
||||
import { ToggleSwitch } from '../ToggleSwitch';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_SUCCESS,
|
||||
} from '../../../utils/input-statuses';
|
||||
import { UpdateArgs } from '../../../types/config-section';
|
||||
import { TEXTFIELD_TYPE_TEXT } from '../TextFieldWithSubmit';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export const ConfigNotify = () => {
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
const { notifications } = serverConfig || {};
|
||||
const { twitter } = notifications || {};
|
||||
|
||||
const [formDataValues, setFormDataValues] = useState<any>({});
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
const [enableSaveButton, setEnableSaveButton] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
enabled,
|
||||
apiKey,
|
||||
apiSecret,
|
||||
accessToken,
|
||||
accessTokenSecret,
|
||||
bearerToken,
|
||||
goLiveMessage,
|
||||
} = twitter || {};
|
||||
setFormDataValues({
|
||||
enabled,
|
||||
apiKey,
|
||||
apiSecret,
|
||||
accessToken,
|
||||
accessTokenSecret,
|
||||
bearerToken,
|
||||
goLiveMessage,
|
||||
});
|
||||
}, [twitter]);
|
||||
|
||||
const canSave = (): boolean => {
|
||||
const { apiKey, apiSecret, accessToken, accessTokenSecret, bearerToken, goLiveMessage } =
|
||||
formDataValues;
|
||||
|
||||
return (
|
||||
!!apiKey &&
|
||||
!!apiSecret &&
|
||||
!!accessToken &&
|
||||
!!accessTokenSecret &&
|
||||
!!bearerToken &&
|
||||
!!goLiveMessage
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setEnableSaveButton(canSave());
|
||||
}, [formDataValues]);
|
||||
|
||||
// update individual values in state
|
||||
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
|
||||
setFormDataValues({
|
||||
...formDataValues,
|
||||
[fieldName]: value,
|
||||
});
|
||||
};
|
||||
|
||||
// toggle switch.
|
||||
const handleSwitchChange = (switchEnabled: boolean) => {
|
||||
const previouslySaved = formDataValues.enabled;
|
||||
|
||||
handleFieldChange({ fieldName: 'enabled', value: switchEnabled });
|
||||
|
||||
return switchEnabled !== previouslySaved;
|
||||
};
|
||||
|
||||
let resetTimer = null;
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
resetTimer = null;
|
||||
clearTimeout(resetTimer);
|
||||
setEnableSaveButton(false);
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
const postValue = formDataValues;
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath: '/notifications/twitter',
|
||||
data: { value: postValue },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({
|
||||
fieldName: 'twitter',
|
||||
value: postValue,
|
||||
path: 'notifications',
|
||||
});
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Twitter</Title>
|
||||
<p className="description reduced-margins">
|
||||
Let your Twitter followers know each time you go live.
|
||||
</p>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<p className="description reduced-margins">
|
||||
<a href="https://owncast.online/docs/notifications" target="_blank" rel="noreferrer">
|
||||
Read how to configure your Twitter account
|
||||
</a>{' '}
|
||||
to support posting from Owncast.
|
||||
</p>
|
||||
<p className="description reduced-margins">
|
||||
<a
|
||||
href="https://developer.twitter.com/en/portal/dashboard"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
And then get your Twitter developer credentials
|
||||
</a>{' '}
|
||||
to fill in below.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ToggleSwitch
|
||||
apiPath=""
|
||||
fieldName="enabled"
|
||||
label="Enable Twitter"
|
||||
onChange={handleSwitchChange}
|
||||
checked={formDataValues.enabled}
|
||||
/>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.apiKey}
|
||||
required
|
||||
value={formDataValues.apiKey}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.apiSecret}
|
||||
type={TEXTFIELD_TYPE_PASSWORD}
|
||||
required
|
||||
value={formDataValues.apiSecret}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.accessToken}
|
||||
required
|
||||
value={formDataValues.accessToken}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.accessTokenSecret}
|
||||
type={TEXTFIELD_TYPE_PASSWORD}
|
||||
required
|
||||
value={formDataValues.accessTokenSecret}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.bearerToken}
|
||||
required
|
||||
value={formDataValues.bearerToken}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.goLiveMessage}
|
||||
type={TEXTFIELD_TYPE_TEXT}
|
||||
required
|
||||
value={formDataValues.goLiveMessage}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={save}
|
||||
style={{
|
||||
display: enableSaveButton ? 'inline-block' : 'none',
|
||||
position: 'relative',
|
||||
marginLeft: 'auto',
|
||||
right: '0',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default ConfigNotify;
|
||||
Reference in New Issue
Block a user