apply config form flow to edit content page
This commit is contained in:
parent
2772a8e5ec
commit
e7e89556e7
@ -60,7 +60,7 @@ export default function TextField(props: TextFieldProps) {
|
||||
setHasChanged(false);
|
||||
clearTimeout(resetTimer);
|
||||
resetTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// if field is required but value is empty, or equals initial value, then don't show submit/update button. otherwise clear out any result messaging and display button.
|
||||
const handleChange = (e: any) => {
|
||||
|
@ -150,6 +150,9 @@ export default function MainLayout(props) {
|
||||
<Menu.Item key="config-storage">
|
||||
<Link href="/config-storage">Storage</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="config-page-content">
|
||||
<Link href="/config-page-content">Custom page content</Link>
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
|
||||
<SubMenu
|
||||
|
105
web/pages/config-page-content.tsx
Normal file
105
web/pages/config-page-content.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { Typography, Button } from "antd";
|
||||
import { FormItemProps } from 'antd/lib/form';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
|
||||
import { ServerStatusContext } from '../utils/server-status-context';
|
||||
import { TEXTFIELD_DEFAULTS, postConfigUpdateToAPI, RESET_TIMEOUT, SUCCESS_STATES} from './components/config/constants';
|
||||
|
||||
import 'react-markdown-editor-lite/lib/index.css';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
const mdParser = new MarkdownIt(/* Markdown-it options */);
|
||||
|
||||
const MdEditor = dynamic(() => import('react-markdown-editor-lite'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function PageContentEditor() {
|
||||
const [content, setContent] = useState('');
|
||||
const [submitStatus, setSubmitStatus] = useState<FormItemProps['validateStatus']>('');
|
||||
const [submitStatusMessage, setSubmitStatusMessage] = useState('');
|
||||
const [hasChanged, setHasChanged] = useState(false);
|
||||
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
|
||||
const { instanceDetails } = serverConfig;
|
||||
const { extraPageContent: initialContent } = instanceDetails;
|
||||
|
||||
const { apiPath } = TEXTFIELD_DEFAULTS.instanceDetails.extraPageContent;
|
||||
|
||||
let resetTimer = null;
|
||||
|
||||
function handleEditorChange({ text }) {
|
||||
setContent(text);
|
||||
if (text !== initialContent && !hasChanged) {
|
||||
setHasChanged(true);
|
||||
} else if (text === initialContent && hasChanged) {
|
||||
setHasChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out any validation states and messaging
|
||||
const resetStates = () => {
|
||||
setSubmitStatus('');
|
||||
setHasChanged(false);
|
||||
clearTimeout(resetTimer);
|
||||
resetTimer = null;
|
||||
};
|
||||
|
||||
// posts all the tags at once as an array obj
|
||||
async function handleSave() {
|
||||
setSubmitStatus('validating');
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath,
|
||||
data: { value: content },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({ fieldName: 'extraPageContent', value: content, path: apiPath });
|
||||
setSubmitStatus('success');
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus('error');
|
||||
setSubmitStatusMessage(`There was an error: ${message}`);
|
||||
},
|
||||
});
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setContent(initialContent);
|
||||
}, [instanceDetails]);
|
||||
|
||||
const {
|
||||
icon: newStatusIcon = null,
|
||||
message: newStatusMessage = '',
|
||||
} = SUCCESS_STATES[submitStatus] || {};
|
||||
|
||||
|
||||
return (
|
||||
<div className="config-page-content-form">
|
||||
<Title level={2}>Edit custom content</Title>
|
||||
|
||||
<p>Add some content about your site with the Markdown editor below. This content shows up at the bottom half of your Owncast page.</p>
|
||||
|
||||
<MdEditor
|
||||
style={{ height: "30em" }}
|
||||
value={content}
|
||||
renderHTML={(c: string) => mdParser.render(c)}
|
||||
onChange={handleEditorChange}
|
||||
config={{
|
||||
htmlClass: 'markdown-editor-preview-pane',
|
||||
markdownClass: 'markdown-editor-pane',
|
||||
}}
|
||||
/>
|
||||
<div className="page-content-actions">
|
||||
{ hasChanged ? <Button type="primary" size="small" onClick={handleSave}>Save</Button> : null }
|
||||
<div className={`status-message ${submitStatus || ''}`}>
|
||||
{newStatusIcon} {newStatusMessage} {submitStatusMessage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -39,7 +39,7 @@ export default function ConfigServerDetails() {
|
||||
configPath: '',
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="config-server-details-form">
|
||||
<Title level={2}>Edit your Server's details</Title>
|
||||
|
||||
<div className="config-public-details-container">
|
||||
@ -53,7 +53,7 @@ export default function ConfigServerDetails() {
|
||||
<TextField fieldName="rtmpServerPort" type={TEXTFIELD_TYPE_NUMBER} {...extraProps} />
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,51 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import MarkdownIt from 'markdown-it';
|
||||
const mdParser = new MarkdownIt(/* Markdown-it options */);
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import 'react-markdown-editor-lite/lib/index.css';
|
||||
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL, UPDATE_CHAT_MESSGAE_VIZ } from "../utils/apis";
|
||||
|
||||
import { Table, Typography, Tooltip, Button } from "antd";
|
||||
|
||||
const MdEditor = dynamic(() => import('react-markdown-editor-lite'), {
|
||||
ssr: false
|
||||
});
|
||||
|
||||
export default function PageContentEditor() {
|
||||
const [content, setContent] = React.useState("");
|
||||
|
||||
function handleEditorChange({ html, text }) {
|
||||
setContent(text);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log(content);
|
||||
alert("Make API call to save here." + content)
|
||||
}
|
||||
|
||||
async function setInitialContent() {
|
||||
const serverConfig = await fetchData(SERVER_CONFIG);
|
||||
const initialContent = serverConfig.instanceDetails.extraPageContent;
|
||||
setContent(initialContent);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setInitialContent();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MdEditor
|
||||
style={{ height: "500px" }}
|
||||
value={content}
|
||||
renderHTML={(content) => mdParser.render(content)}
|
||||
onChange={handleEditorChange}
|
||||
config={{ htmlClass: 'markdown-editor-preview-pane', markdownClass: 'markdown-editor-pane' }}
|
||||
/>
|
||||
<Button onClick={handleSave}>Save</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -143,3 +143,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.config-page-content-form {
|
||||
.page-content-actions {
|
||||
margin-top: 1em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.status-message {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,16 +80,16 @@ code {
|
||||
.rc-md-editor {
|
||||
// Set the background color of the preview container
|
||||
.editor-container {
|
||||
background-color: black;
|
||||
background-color: #E2E8F0;
|
||||
color: rgba(45,55,72,1);
|
||||
}
|
||||
|
||||
// Custom CSS for formatting the preview text
|
||||
.markdown-editor-preview-pane {
|
||||
color:lightgrey;
|
||||
// color:lightgrey;
|
||||
a {
|
||||
color: $owncast-purple;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
@ -97,12 +97,15 @@ code {
|
||||
|
||||
// Custom CSS class used to format the text of the editor
|
||||
.markdown-editor-pane {
|
||||
color:lightgrey !important;
|
||||
color: white !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;
|
||||
}
|
||||
|
||||
// Hide extra toolbar buttons.
|
||||
|
@ -9,6 +9,8 @@ export interface TextFieldProps {
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
onSubmit?: () => void;
|
||||
onBlur?: () => void;
|
||||
onChange?: () => void;
|
||||
}
|
||||
|
||||
export interface ToggleSwitchProps {
|
||||
@ -49,7 +51,7 @@ export interface ConfigInstanceDetailsFields {
|
||||
|
||||
export interface VideoVariant {
|
||||
audioBitrate: number;
|
||||
audioPassthrough: number;
|
||||
audioPassthrough: false | number;
|
||||
encoderPreset: 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast';
|
||||
framerate: number;
|
||||
videoBitrate: number;
|
||||
|
@ -25,6 +25,8 @@ export const initialServerConfigState: ConfigDetails = {
|
||||
instanceUrl: '',
|
||||
},
|
||||
videoSettings: {
|
||||
numberOfPlaylistItems: 5,
|
||||
segmentLengthSeconds: 4,
|
||||
videoQualityVariants: [
|
||||
{
|
||||
audioPassthrough: false,
|
||||
@ -32,6 +34,7 @@ export const initialServerConfigState: ConfigDetails = {
|
||||
videoBitrate: 0,
|
||||
audioBitrate: 0,
|
||||
framerate: 0,
|
||||
encoderPreset: 'veryfast',
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -93,7 +96,7 @@ const ServerStatusProvider = ({ children }) => {
|
||||
};
|
||||
|
||||
setConfig(updatedConfig);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@ -108,7 +111,7 @@ const ServerStatusProvider = ({ children }) => {
|
||||
return () => {
|
||||
clearInterval(getStatusIntervalId);
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
const providerValue = {
|
||||
...status,
|
||||
|
Loading…
x
Reference in New Issue
Block a user