From a122ee6c4282e75bfaa16c3b81225a01471ce325 Mon Sep 17 00:00:00 2001 From: gingervitis Date: Fri, 12 Feb 2021 23:55:59 -0800 Subject: [PATCH] Admin css overhaul pt2 (#19) * tweaks to offline state in admin viewers page If stream is offline, hide current viewers statistic and viewers table. Also, change wording for describing max viewers. * take out ant dark stylesheet, organize ant color overrides * remove ant dark css; cleanup ant overrides; format public-detail page * combine toggleswitch component style with textfield so layout can be shared * fix toggleswitch status message placement * - update styles for modals, collapses - move reset dir into its own component - assorted style cleanups ans consistencies * hide entire advanced section for resetyp if no yp * temp adjustments to video modal * temp comment out toggle switch use for later' * address PR comments * lint * update type * allow warnings during lint Co-authored-by: nebunez --- web/.github/workflows/linter.yml | 1 - web/components/config/cpu-usage.tsx | 17 +- web/components/config/edit-directory.tsx | 6 +- .../config/edit-instance-details.tsx | 106 ++++-- .../config/edit-page-content.tsx} | 25 +- web/components/config/edit-server-details.tsx | 55 +-- web/components/config/edit-social-links.tsx | 42 +-- web/components/config/edit-storage.tsx | 8 + web/components/config/edit-tags.tsx | 12 +- .../config/form-textfield-with-submit.tsx | 2 +- web/components/config/form-textfield.tsx | 10 +- .../config/form-toggleswitch-with-submit.tsx | 90 +++-- web/components/config/reset-yp.tsx | 42 +++ .../config/social-icons-dropdown.tsx | 4 +- web/components/config/video-latency.tsx | 28 +- web/components/config/video-variant-form.tsx | 217 +++++------ .../config/video-variants-table.tsx | 41 +- web/components/main-layout.tsx | 18 +- web/package-lock.json | 29 ++ web/package.json | 1 + web/pages/_app.tsx | 4 +- web/pages/config-public-details.tsx | 36 +- web/pages/config-server-details.tsx | 12 +- web/pages/config-storage.tsx | 8 +- web/pages/config-video.tsx | 24 +- web/styles/ant-overrides.scss | 357 +++++++++++++++--- web/styles/colors.scss | 25 +- web/styles/config-public-details.scss | 65 ++++ web/styles/config-socialhandles.scss | 32 ++ web/styles/config-storage.scss | 3 +- web/styles/config-tags.scss | 18 +- web/styles/config-video-variants.scss | 120 +++--- web/styles/config.scss | 29 +- web/styles/form-misc-elements.scss | 29 +- web/styles/form-textfields.scss | 49 ++- web/styles/form-toggleswitch.scss | 29 -- web/styles/globals.scss | 62 ++- web/styles/main-layout.scss | 6 + web/styles/markdown-editor.scss | 27 +- web/utils/config-constants.tsx | 13 - 40 files changed, 1150 insertions(+), 552 deletions(-) rename web/{pages/config-page-content.tsx => components/config/edit-page-content.tsx} (86%) create mode 100644 web/components/config/reset-yp.tsx create mode 100644 web/styles/config-public-details.scss delete mode 100644 web/styles/form-toggleswitch.scss diff --git a/web/.github/workflows/linter.yml b/web/.github/workflows/linter.yml index 6e9248194..b2ed8586f 100644 --- a/web/.github/workflows/linter.yml +++ b/web/.github/workflows/linter.yml @@ -20,4 +20,3 @@ jobs: config-path: '.eslintrc.js' ignore-path: '.eslintignore' extensions: 'ts,tsx,js,jsx' - extra-args: '--max-warnings=0' \ No newline at end of file diff --git a/web/components/config/cpu-usage.tsx b/web/components/config/cpu-usage.tsx index f7d82a194..3f3a7a06c 100644 --- a/web/components/config/cpu-usage.tsx +++ b/web/components/config/cpu-usage.tsx @@ -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 (
- CPU Usage -

There are trade-offs when considering CPU usage blah blah more wording here.

-
+ + CPU Usage + +

+ There are trade-offs when considering CPU usage blah blah more wording here. +


+
TOOLTIPS[value]} diff --git a/web/components/config/edit-directory.tsx b/web/components/config/edit-directory.tsx index 3a56cdd5f..033908daf 100644 --- a/web/components/config/edit-directory.tsx +++ b/web/components/config/edit-directory.tsx @@ -33,9 +33,11 @@ export default function EditYPDetails() { } return (
- Owncast Directory Settings + + Owncast Directory Settings + -

+

Would you like to appear in the{' '} Owncast Directory diff --git a/web/components/config/edit-instance-details.tsx b/web/components/config/edit-instance-details.tsx index 7ef694f25..92f280223 100644 --- a/web/components/config/edit-instance-details.tsx +++ b/web/components/config/edit-instance-details.tsx @@ -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 ( -

- diff --git a/web/pages/config-page-content.tsx b/web/components/config/edit-page-content.tsx similarity index 86% rename from web/pages/config-page-content.tsx rename to web/components/config/edit-page-content.tsx index 672308d84..ee12a403d 100644 --- a/web/pages/config-page-content.tsx +++ b/web/components/config/edit-page-content.tsx @@ -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(null); const [hasChanged, setHasChanged] = useState(false); @@ -83,10 +84,12 @@ export default function PageContentEditor() { }, [instanceDetails]); return ( -
- Page Content +
+ + Custom Page Content + -

+

Edit the content of your page by using simple{' '} Markdown syntax.

diff --git a/web/components/config/edit-server-details.tsx b/web/components/config/edit-server-details.tsx index 6bbecfc9d..92541625d 100644 --- a/web/components/config/edit-server-details.tsx +++ b/web/components/config/edit-server-details.tsx @@ -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 ( -
- Reset Directory: - - - -

- 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. -

-
- ); - // } - // 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} /> - - -
+ + {yp.enabled && ( + + -
-
-
+ + + )}
); } diff --git a/web/components/config/edit-social-links.tsx b/web/components/config/edit-social-links.tsx index 2396f1121..6e5a75e18 100644 --- a/web/components/config/edit-social-links.tsx +++ b/web/components/config/edit-social-links.tsx @@ -165,43 +165,36 @@ export default function EditSocialLinks() { const socialHandlesColumns: ColumnsType = [ { - 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 ( - <> +
- {platformName} - +

+ {platformName} + {url} +

+
); }, }, - - { - title: 'Url Link', - dataIndex: 'url', - key: 'url', - }, { title: '', dataIndex: '', key: 'edit', render: (data, record, index) => { return ( - +
); }, }, @@ -231,12 +224,17 @@ export default function EditSocialLinks() { return (
-

Add all your social media handles and links to your other profiles here.

+ + Your Social Handles + +

+ Add all your social media handles and links to your other profiles here. +

record.url} diff --git a/web/components/config/edit-storage.tsx b/web/components/config/edit-storage.tsx index 129b31811..3abb5e967 100644 --- a/web/components/config/edit-storage.tsx +++ b/web/components/config/edit-storage.tsx @@ -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 (
+ {/* */} (''); const [submitStatus, setSubmitStatus] = useState(null); @@ -100,8 +102,12 @@ export default function EditInstanceTags() { return (
- Add Tags -

This is a great way to categorize your Owncast server on the Directory!

+ + Add Tags + +

+ This is a great way to categorize your Owncast server on the Directory! +

{tags.map((tag, index) => { @@ -109,7 +115,7 @@ export default function EditInstanceTags() { handleDeleteTag(index); }; return ( - + {tag} ); diff --git a/web/components/config/form-textfield-with-submit.tsx b/web/components/config/form-textfield-with-submit.tsx index 3804df7fa..00105daec 100644 --- a/web/components/config/form-textfield-with-submit.tsx +++ b/web/components/config/form-textfield-with-submit.tsx @@ -120,7 +120,7 @@ export default function TextFieldWithSubmit(props: TextFieldWithSubmitProps) { onChange={handleChange} />
-
+

{tip}
diff --git a/web/components/config/form-textfield.tsx b/web/components/config/form-textfield.tsx index 8b0862be7..c0523f141 100644 --- a/web/components/config/form-textfield.tsx +++ b/web/components/config/form-textfield.tsx @@ -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) {
{label ? (
-
@@ -140,10 +141,7 @@ export default function TextField(props: TextFieldProps) { />
-

- {tip} - {/* */} -

+

{tip}

); @@ -151,9 +149,7 @@ export default function TextField(props: TextFieldProps) { TextField.defaultProps = { className: '', - // configPath: '', disabled: false, - // initialValue: '', label: '', maxLength: 255, diff --git a/web/components/config/form-toggleswitch-with-submit.tsx b/web/components/config/form-toggleswitch-with-submit.tsx index 942da041c..0ee264d13 100644 --- a/web/components/config/form-toggleswitch-with-submit.tsx +++ b/web/components/config/form-toggleswitch-with-submit.tsx @@ -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(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 ( -
-
- - - {label} - +
+ {label && ( +
+ {label} +
+ )} + +
+
+ + +
+

{tip}

-
); } @@ -87,4 +111,6 @@ ToggleSwitch.defaultProps = { disabled: false, label: '', tip: '', + useSubmit: false, + onChange: null, }; diff --git a/web/components/config/reset-yp.tsx b/web/components/config/reset-yp.tsx new file mode 100644 index 000000000..97d3ad79d --- /dev/null +++ b/web/components/config/reset-yp.tsx @@ -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 ( + <> + + Reset Directory + +

+ 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. +

+ + + + + + ); +} diff --git a/web/components/config/social-icons-dropdown.tsx b/web/components/config/social-icons-dropdown.tsx index cb3a621d8..dec12cdf0 100644 --- a/web/components/config/social-icons-dropdown.tsx +++ b/web/components/config/social-icons-dropdown.tsx @@ -19,11 +19,11 @@ export default function SocialDropdown({ iconList, selectedOption, onSelected }: const inititalSelected = selectedOption === '' ? null : selectedOption; return (
-

+

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.

-

+

If you DO have a logo, drop it in to the /webroot/img/platformicons directory and update the /socialHandle.go list. Then restart the server and it will show up in the list. diff --git a/web/components/config/video-latency.tsx b/web/components/config/video-latency.tsx index c213e0f6d..39b7e1e3c 100644 --- a/web/components/config/video-latency.tsx +++ b/web/components/config/video-latency.tsx @@ -46,9 +46,6 @@ function SegmentToolTip({ value }: SegmentToolTipProps) { export default function VideoLatency() { const [submitStatus, setSubmitStatus] = useState(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 (

- Latency Buffer -

- While it's natural to want to keep your latency as low as possible, you may experience + + Latency Buffer + +

+ 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.

- For interactive live streams you may want to experiment with a lower latency, for - non-interactive broadcasts you may want to increase it.{' '} - Read to learn more. -

+

+ For interactive live streams you may want to experiment with a lower latency, for + non-interactive broadcasts you may want to increase it.{' '} + Read to learn more. +

+
} @@ -132,8 +128,8 @@ export default function VideoLatency() { defaultValue={selectedOption} value={selectedOption} /> +
-
); } diff --git a/web/components/config/video-variant-form.tsx b/web/components/config/video-variant-form.tsx index 5e0035b65..79db89d35 100644 --- a/web/components/config/video-variant-form.tsx +++ b/web/components/config/video-variant-form.tsx @@ -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 (
-
+

Say a thing here about how this all works. Read more{' '} here. -
-
-

+

- {/* ENCODER PRESET FIELD */} -
-
- - {selectedPresetNote ? ( - {selectedPresetNote} - ) : null} -
-
- - {/* VIDEO PASSTHROUGH FIELD */} -
-
-

- - Use Video Passthrough? -

-
- -
-
-
- - {/* VIDEO BITRATE FIELD */} -
-

- - Video Bitrate: -

-
- `${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 ? ( - {selectedVideoBRnote} - ) : null} -
-
- - - -
- 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 or the height to keep your aspect ratio. -
-
- -
-
- +
+ {/* ENCODER PRESET FIELD */} +
+ + {selectedPresetNote && ( + {selectedPresetNote} + )}
- {/* FRAME RATE FIELD */} -
+ {/* VIDEO PASSTHROUGH FIELD */} +

- - Frame rate: + + Use Video Passthrough?

- `${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 */} + - {selectedFramerateNote ? ( - {selectedFramerateNote} - ) : null}
- - + + {/* VIDEO BITRATE FIELD */} +
+ + Video Bitrate + +

{VIDEO_VARIANT_DEFAULTS.videoBitrate.tip}

+
+ `${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 && ( + {selectedVideoBRnote} + )} +
+
+
+ + +
+ 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 or the height to keep your aspect ratio. +
+
+ +
+
+ +
+ + {/* FRAME RATE FIELD */} +
+

+ + Frame rate: +

+
+ `${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 ? ( + {selectedFramerateNote} + ) : null} +
+
+
+
+
); } diff --git a/web/components/config/video-variants-table.tsx b/web/components/config/video-variants-table.tsx index c22e62118..d8e1a2a11 100644 --- a/web/components/config/video-variants-table.tsx +++ b/web/components/config/video-variants-table.tsx @@ -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(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 = [ { title: 'Video bitrate', @@ -176,12 +178,6 @@ export default function CurrentVariantsTable() { }, ]; - const statusMessage = ( -
- {newStatusIcon} {newStatusMessage} {submitStatusMessage} -
- ); - const videoQualityVariantData = videoQualityVariants.map((variant, index) => ({ key: index + 1, ...variant, @@ -189,9 +185,11 @@ export default function CurrentVariantsTable() { return ( <> - Stream output + + Stream output + - {statusMessage} +
- {statusMessage} +