Factor out 'FormFieldDuration' for re-use
This commit is contained in:
parent
085fe44463
commit
ced33fb5ef
3 changed files with 94 additions and 60 deletions
3
ui/component/formFieldDuration/index.js
Normal file
3
ui/component/formFieldDuration/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import FormFieldDuration from './view';
|
||||
|
||||
export default FormFieldDuration;
|
88
ui/component/formFieldDuration/view.jsx
Normal file
88
ui/component/formFieldDuration/view.jsx
Normal file
|
@ -0,0 +1,88 @@
|
|||
// @flow
|
||||
import type { Node } from 'react';
|
||||
import React from 'react';
|
||||
import parseDuration from 'parse-duration';
|
||||
import { FormField } from 'component/common/form';
|
||||
import Icon from 'component/common/icon';
|
||||
import * as ICONS from 'constants/icons';
|
||||
|
||||
const INPUT_EXAMPLES = '\n- 30s\n- 10m\n- 1h\n- 2d\n- 3mo\n- 1y';
|
||||
const ONE_HUNDRED_YEARS_IN_SECONDS = 3154000000;
|
||||
|
||||
type Props = {
|
||||
name: string,
|
||||
label?: string | Node,
|
||||
placeholder?: string | number,
|
||||
value: string | number,
|
||||
onChange: (any) => void,
|
||||
onResolve: (valueInSeconds: number) => void, // Returns parsed/resolved value in seconds; "-1" for invalid input.
|
||||
maxDurationInSeconds?: number,
|
||||
};
|
||||
|
||||
export default function FormFieldDuration(props: Props) {
|
||||
const { name, label, placeholder, value, onChange, onResolve, maxDurationInSeconds } = props;
|
||||
const [valueSec, setValueSec] = React.useState(-1);
|
||||
const [valueErr, setValueErr] = React.useState('');
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleInvalidInput = (errMsg: string) => {
|
||||
if (valueSec !== -1) {
|
||||
setValueSec(-1);
|
||||
onResolve(-1);
|
||||
}
|
||||
if (valueErr !== errMsg) {
|
||||
setValueErr(errMsg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleValidInput = (seconds) => {
|
||||
if (seconds !== valueSec) {
|
||||
setValueSec(seconds);
|
||||
onResolve(seconds);
|
||||
}
|
||||
if (valueErr) {
|
||||
setValueErr('');
|
||||
}
|
||||
};
|
||||
|
||||
if (!value) {
|
||||
handleValidInput(-1); // Reset
|
||||
return;
|
||||
}
|
||||
|
||||
const seconds = parseDuration(value, 's');
|
||||
if (Number.isInteger(seconds) && seconds > 0) {
|
||||
const max = maxDurationInSeconds || ONE_HUNDRED_YEARS_IN_SECONDS;
|
||||
if (seconds > max) {
|
||||
handleInvalidInput(__('Value exceeded maximum.'));
|
||||
} else {
|
||||
handleValidInput(seconds);
|
||||
}
|
||||
} else {
|
||||
handleInvalidInput(__('Invalid duration.'));
|
||||
}
|
||||
}, [value, valueSec, valueErr, maxDurationInSeconds, onResolve]);
|
||||
|
||||
return (
|
||||
<FormField
|
||||
name={name}
|
||||
type="text"
|
||||
label={
|
||||
<>
|
||||
{label || __('Duration')}
|
||||
<Icon
|
||||
customTooltipText={__('Examples: %examples%', { examples: INPUT_EXAMPLES })}
|
||||
className="icon--help"
|
||||
icon={ICONS.HELP}
|
||||
tooltip
|
||||
size={16}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
placeholder={placeholder || '30s, 10m, 1h, 2d, 3mo, 1y'}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={valueErr}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import parseDuration from 'parse-duration';
|
||||
import Button from 'component/button';
|
||||
import ChannelThumbnail from 'component/channelThumbnail';
|
||||
import ClaimPreview from 'component/claimPreview';
|
||||
import Card from 'component/common/card';
|
||||
import { FormField } from 'component/common/form';
|
||||
import Icon from 'component/common/icon';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import FormFieldDuration from 'component/formFieldDuration';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { getChannelFromClaim } from 'util/claim';
|
||||
|
@ -67,7 +65,6 @@ export default function ModalBlockChannel(props: Props) {
|
|||
const [tab, setTab] = usePersistedState('ModalBlockChannel:tab', TAB.PERSONAL);
|
||||
const [blockType, setBlockType] = usePersistedState('ModalBlockChannel:blockType', BLOCK.PERMANENT);
|
||||
const [timeoutInput, setTimeoutInput] = usePersistedState('ModalBlockChannel:timeoutInput', '10m');
|
||||
const [timeoutInputErr, setTimeoutInputErr] = React.useState('');
|
||||
const [timeoutSec, setTimeoutSec] = React.useState(-1);
|
||||
|
||||
const isPersonalTheOnlyTab = !activeChannelIsModerator && !activeChannelIsAdmin;
|
||||
|
@ -92,45 +89,6 @@ export default function ModalBlockChannel(props: Props) {
|
|||
}
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// 'timeoutInput' to 'timeoutSec' conversion.
|
||||
React.useEffect(() => {
|
||||
const handleInvalidInput = (errMsg: string) => {
|
||||
if (timeoutSec !== -1) {
|
||||
setTimeoutSec(-1);
|
||||
}
|
||||
if (timeoutInputErr !== errMsg) {
|
||||
setTimeoutInputErr(errMsg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleValidInput = (seconds) => {
|
||||
if (seconds !== timeoutSec) {
|
||||
setTimeoutSec(seconds);
|
||||
}
|
||||
if (timeoutInputErr) {
|
||||
setTimeoutInputErr('');
|
||||
}
|
||||
};
|
||||
|
||||
if (!timeoutInput) {
|
||||
handleValidInput(-1); // Reset
|
||||
return;
|
||||
}
|
||||
|
||||
const ONE_HUNDRED_YEARS_IN_SECONDS = 3154000000;
|
||||
const seconds = parseDuration(timeoutInput, 's');
|
||||
|
||||
if (Number.isInteger(seconds) && seconds > 0) {
|
||||
if (seconds > ONE_HUNDRED_YEARS_IN_SECONDS) {
|
||||
handleInvalidInput(__('Wow, banned for more than 100 years?'));
|
||||
} else {
|
||||
handleValidInput(seconds);
|
||||
}
|
||||
} else {
|
||||
handleInvalidInput(__('Invalid duration.'));
|
||||
}
|
||||
}, [timeoutInput, timeoutInputErr, timeoutSec]);
|
||||
|
||||
// **************************************************************************
|
||||
// **************************************************************************
|
||||
|
||||
|
@ -178,27 +136,12 @@ export default function ModalBlockChannel(props: Props) {
|
|||
}
|
||||
|
||||
function getTimeoutDurationElem() {
|
||||
const examples = '\n- 30s\n- 10m\n- 1h\n- 2d\n- 3mo\n- 1y';
|
||||
return (
|
||||
<FormField
|
||||
<FormFieldDuration
|
||||
name="time_out"
|
||||
label={
|
||||
<>
|
||||
{__('Duration')}
|
||||
<Icon
|
||||
customTooltipText={__('Enter the timeout duration. Examples: %examples%', { examples })}
|
||||
className="icon--help"
|
||||
icon={ICONS.HELP}
|
||||
tooltip
|
||||
size={16}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
type="text"
|
||||
placeholder="30s, 10m, 1h, 2d, 3mo, 1y"
|
||||
value={timeoutInput}
|
||||
onChange={(e) => setTimeoutInput(e.target.value)}
|
||||
error={timeoutInputErr}
|
||||
onResolve={(valueInSeconds) => setTimeoutSec(valueInSeconds)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue