fixes for various admin issues (#181)

* up max char count for variant name to fix https://github.com/owncast/owncast/issues/1037

* max widthing the line chart canvas size so it scales with the page.
fixes
- https://github.com/owncast/owncast/issues/842
- https://github.com/owncast/owncast/issues/1024

* A fix to make Storage Endpoint URL validation have better feedback.

- give the field a type of "url"
- give the field a pattern to check
- have native browser handle the validation
- if the field is invalid, use :invalid selector to turn the text red on blur.

fixes: https://github.com/owncast/owncast/issues/1000

* a fix for https://github.com/owncast/owncast/issues/874

* - fixes for https://github.com/owncast/owncast/issues/972
Add optional prop to text field to trim() whitespaces from field. Apply logic to mostly url fields.

- move textfield blur if invalid turn red to globaal

* - a fix for bug:  https://github.com/owncast/owncast/issues/998
don't return null if platform name not found because its custom.

- clean up react key problem on socialhandles table

* fix react key issue on Actions table

* fix for https://github.com/owncast/owncast/issues/1008 to display 'other' field when editing an item not in predefined social list

* clean up other potential react key warnings

* Prettified Code!

Co-authored-by: gingervitis <gingervitis@users.noreply.github.com>
This commit is contained in:
gingervitis
2021-05-22 23:27:51 -07:00
committed by GitHub
parent 8d7f11b4ef
commit d0eb1446f3
15 changed files with 132 additions and 43 deletions

View File

@@ -13,7 +13,7 @@ import {
OTHER_SOCIAL_HANDLE_OPTION,
} from '../../utils/config-constants';
import { SocialHandle, UpdateArgs } from '../../types/config-section';
import isValidUrl from '../../utils/urls';
import isValidUrl, { DEFAULT_TEXTFIELD_URL_PATTERN } from '../../utils/urls';
import TextField from './form-textfield';
import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses';
import FormStatusIndicator from './form-status-indicator';
@@ -62,6 +62,10 @@ export default function EditSocialLinks() {
}
};
const isPredefinedSocial = (platform: string) => {
return availableIconsList.find(item => item.key === platform) || false;
};
const selectedOther =
modalDataState.platform !== '' &&
!availableIconsList.find(item => item.key === modalDataState.platform);
@@ -172,9 +176,18 @@ export default function EditSocialLinks() {
key: 'combo',
render: (data, record) => {
const { platform, url } = record;
const platformInfo = availableIconsList.find(item => item.key === platform);
const platformInfo = isPredefinedSocial(platform);
// custom platform case
if (!platformInfo) {
return platform;
return (
<div className="social-handle-cell">
<p className="option-label">
<strong>{platform}</strong>
<span className="handle-url" title={url}>{url}</span>
</p>
</div>
);
}
const { icon, platform: platformName } = platformInfo;
const iconUrl = NEXT_PUBLIC_API_HOST + `${icon.slice(1)}`;
@@ -201,9 +214,13 @@ export default function EditSocialLinks() {
<Button
size="small"
onClick={() => {
const platformInfo = currentSocialHandles[index];
setEditId(index);
setModalDataState({ ...currentSocialHandles[index] });
setModalDataState({ ...platformInfo });
setDisplayModal(true);
if (!isPredefinedSocial(platformInfo.platform)) {
setDisplayOther(true);
}
}}
>
Edit
@@ -251,7 +268,7 @@ export default function EditSocialLinks() {
className="social-handles-table"
pagination={false}
size="small"
rowKey={record => record.url}
rowKey={record => `${record.platform}-${record.url}`}
columns={socialHandlesColumns}
dataSource={currentSocialHandles}
/>
@@ -278,6 +295,9 @@ export default function EditSocialLinks() {
placeholder={PLACEHOLDERS[modalDataState.platform] || 'Url to page'}
value={modalDataState.url}
onChange={handleUrlChange}
useTrim
type="url"
pattern={DEFAULT_TEXTFIELD_URL_PATTERN}
/>
<FormStatusIndicator status={submitStatus} />
</div>

View File

@@ -41,6 +41,7 @@ export default function TextFieldWithSubmit(props: TextFieldWithSubmitProps) {
apiPath,
configPath = '',
initialValue,
useTrim,
...textFieldProps // rest of props
} = props;
@@ -70,7 +71,7 @@ export default function TextFieldWithSubmit(props: TextFieldWithSubmitProps) {
// 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 = ({ fieldName: changedFieldName, value: changedValue }: UpdateArgs) => {
if (onChange) {
onChange({ fieldName: changedFieldName, value: changedValue });
onChange({ fieldName: changedFieldName, value: useTrim ? changedValue.trim() : changedValue });
}
};

View File

@@ -22,11 +22,13 @@ export interface TextFieldProps {
disabled?: boolean;
label?: string;
maxLength?: number;
pattern?: string;
placeholder?: string;
required?: boolean;
status?: StatusState;
tip?: string;
type?: string;
useTrim?: boolean;
value?: string | number;
onBlur?: FieldUpdaterFunc;
onChange?: FieldUpdaterFunc;
@@ -42,20 +44,21 @@ export default function TextField(props: TextFieldProps) {
onBlur,
onChange,
onPressEnter,
pattern,
placeholder,
required,
status,
tip,
type,
useTrim,
value,
} = props;
// 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) => {
const val = type === TEXTFIELD_TYPE_NUMBER ? e : e.target.value;
// if an extra onChange handler was sent in as a prop, let's run that too.
if (onChange) {
onChange({ fieldName, value: val });
const val = type === TEXTFIELD_TYPE_NUMBER ? e : e.target.value;
onChange({ fieldName, value: useTrim ? val.trim() : val });
}
};
@@ -100,6 +103,7 @@ export default function TextField(props: TextFieldProps) {
} else if (type === TEXTFIELD_TYPE_URL) {
fieldProps = {
type: 'url',
pattern,
};
}
@@ -114,6 +118,7 @@ export default function TextField(props: TextFieldProps) {
required,
[`status-${statusType}`]: status,
});
return (
<div className={containerClass}>
{label ? (
@@ -130,7 +135,7 @@ export default function TextField(props: TextFieldProps) {
id={fieldId}
className={`field ${className} ${fieldId}`}
{...fieldProps}
allowClear
{...(type !== TEXTFIELD_TYPE_NUMBER && { allowClear: true })}
placeholder={placeholder}
maxLength={maxLength}
onChange={handleChange}

View File

@@ -120,7 +120,7 @@ export default function CodecSelector() {
<Title level={3} className="section-title">
Video Codec
</Title>
<p className="description">
<div className="description">
If you have access to specific hardware with the drivers and software installed for them,
you may be able to improve your video encoding performance.
<p>
@@ -133,7 +133,7 @@ export default function CodecSelector() {
unplayable.
</a>
</p>
</p>
</div>
<div className="segment-slider-container">
<Popconfirm
title={`Are you sure you want to change your video codec to ${pendingSaveCodec} and understand what this means?`}

View File

@@ -153,7 +153,7 @@ export default function VideoVariantForm({
<p className="selected-value-note">{cpuUsageNote()}</p>
</div>
<p className="read-more-subtext">
This could mean GPU or CPU usage depending on your server environment. {' '}
This could mean GPU or CPU usage depending on your server environment.{' '}
<a
href="https://owncast.online/docs/video/?source=admin#cpu-usage"
target="_blank"