Merge in DNS/adguard-home from AG-28194-plain-dns to master Squashed commit of the following: commit a033982b949217d46a8ea609f63198916f779a61 Merge: 03fc2821179d7a1ef4Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Dec 12 12:07:39 2023 +0200 Merge remote-tracking branch 'origin/master' into AG-28194-plain-dns commit 03fc282119a6372fcb4ce17a5d89779ad84589f5 Merge: e31a6593134a34dc05Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Dec 12 11:07:46 2023 +0200 Merge remote-tracking branch 'origin/master' into AG-28194-plain-dns # Conflicts: # CHANGELOG.md commit e31a659312fffe0cd5f57710843c8a6818515502 Merge: 0b735eb427b5cce517Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Dec 11 11:09:07 2023 +0200 Merge remote-tracking branch 'origin/master' into AG-28194-plain-dns # Conflicts: # CHANGELOG.md commit 0b735eb4261883961058aed562c1e72ad1a20915 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 8 15:22:27 2023 +0200 Revert "safesearch: imp docs" This reverts commit bab6bf3467f8914a34413bbbcdc37e89ff0401a5. commit bab6bf3467f8914a34413bbbcdc37e89ff0401a5 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 8 15:21:23 2023 +0200 safesearch: imp docs commit aa5e6e30e01bf947d645ac4a9578eeac09c92a19 Merge: 503888447 2b62901fe Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 8 14:48:13 2023 +0200 Merge remote-tracking branch 'origin/AG-28194-plain-dns' into AG-28194-plain-dns commit 503888447aaf30d48c3fb9a414e8a65beb1a4e23 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 8 14:47:23 2023 +0200 home: imp code commit 2b62901feb29c9613ae648fa5e83598157207a17 Author: Ildar Kamalov <ik@adguard.com> Date: Fri Dec 8 11:55:25 2023 +0300 client: add plain dns description commit 3d51fc8ea1955e599953070a4b330dd4e2fd44bc Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 8 10:15:53 2023 +0200 all: changelog commit 59697b5f1ab049bd2259ffe42cef7223531ef7aa Merge: 81a15d081b668c04eaAuthor: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 8 10:11:59 2023 +0200 Merge remote-tracking branch 'origin/master' into AG-28194-plain-dns commit 81a15d0818b18f99e651311a8502082b4a539e4b Author: Natalia Sokolova <n.sokolova@adguard.com> Date: Thu Dec 7 17:30:05 2023 +0300 client/src/__locales/en.json edited online with Bitbucket commit 0cf2f880fbd1592c02e6df42319cba357f0d7bc8 Author: Natalia Sokolova <n.sokolova@adguard.com> Date: Thu Dec 7 17:29:51 2023 +0300 client/src/__locales/en.json edited online with Bitbucket commit 2f32c59b8b1d764d060a69c35787566cf5210063 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Dec 7 13:14:04 2023 +0200 home: imp code commit 01e21a26bdd13c42c55c8ea3b5bbe84933bf0c04 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Dec 7 12:14:02 2023 +0200 all: imp docs commit b6beec6df7c2a9077ddce018656c701b7e875b53 Author: Ildar Kamalov <ik@adguard.com> Date: Thu Dec 7 12:42:21 2023 +0300 client: fix reset settings commit 93448500d56a4652a3a060b274936c40015ac8ec Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Dec 7 10:55:25 2023 +0200 home: imp code commit eb32f8268bee097a81463ba29f7ea52be6e7d88b Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Dec 7 10:42:23 2023 +0200 home: imp code commit 873d1412cf7c07ed985985a47325779bcfbf650a Merge: 627659680214175eb4Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Dec 7 10:22:25 2023 +0200 Merge remote-tracking branch 'origin/master' into AG-28194-plain-dns commit 627659680da8e973a3878d1722b276d30c7a27bb Author: Ildar Kamalov <ik@adguard.com> Date: Wed Dec 6 17:39:14 2023 +0300 client: handle plain dns setting commit ffdbf05fede721d271a84482a5759284d18eb189 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Dec 1 15:12:50 2023 +0200 home: http dns plain ... and 1 more commit
315 lines
8.7 KiB
JavaScript
315 lines
8.7 KiB
JavaScript
import React, { Fragment } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { Trans } from 'react-i18next';
|
|
import cn from 'classnames';
|
|
|
|
import { createOnBlurHandler } from './helpers';
|
|
import { R_MAC_WITHOUT_COLON, R_UNIX_ABSOLUTE_PATH, R_WIN_ABSOLUTE_PATH } from './constants';
|
|
|
|
export const renderField = (props, elementType) => {
|
|
const {
|
|
input, id, className, placeholder, type, disabled, normalizeOnBlur, onScroll,
|
|
autoComplete, meta: { touched, error }, min, max, step,
|
|
} = props;
|
|
|
|
const onBlur = (event) => createOnBlurHandler(event, input, normalizeOnBlur);
|
|
|
|
const element = React.createElement(elementType, {
|
|
...input,
|
|
id,
|
|
className,
|
|
placeholder,
|
|
autoComplete,
|
|
disabled,
|
|
type,
|
|
min,
|
|
max,
|
|
step,
|
|
onBlur,
|
|
onScroll,
|
|
});
|
|
|
|
return (
|
|
<>
|
|
{element}
|
|
{!disabled && touched && error
|
|
&& <span className="form__message form__message--error"><Trans>{error}</Trans></span>}
|
|
</>
|
|
);
|
|
};
|
|
|
|
renderField.propTypes = {
|
|
id: PropTypes.string.isRequired,
|
|
input: PropTypes.object.isRequired,
|
|
className: PropTypes.string,
|
|
placeholder: PropTypes.string,
|
|
type: PropTypes.string,
|
|
disabled: PropTypes.bool,
|
|
autoComplete: PropTypes.bool,
|
|
normalizeOnBlur: PropTypes.func,
|
|
min: PropTypes.number,
|
|
max: PropTypes.number,
|
|
step: PropTypes.number,
|
|
onScroll: PropTypes.func,
|
|
meta: PropTypes.shape({
|
|
touched: PropTypes.bool,
|
|
error: PropTypes.string,
|
|
}).isRequired,
|
|
};
|
|
|
|
export const renderTextareaField = (props) => renderField(props, 'textarea');
|
|
|
|
export const renderInputField = (props) => renderField(props, 'input');
|
|
|
|
export const renderGroupField = ({
|
|
input,
|
|
id,
|
|
className,
|
|
placeholder,
|
|
type,
|
|
disabled,
|
|
autoComplete,
|
|
isActionAvailable,
|
|
removeField,
|
|
meta: { touched, error },
|
|
normalizeOnBlur,
|
|
}) => {
|
|
const onBlur = (event) => createOnBlurHandler(event, input, normalizeOnBlur);
|
|
|
|
return (
|
|
<>
|
|
<div className="input-group">
|
|
<input
|
|
{...input}
|
|
id={id}
|
|
placeholder={placeholder}
|
|
type={type}
|
|
className={className}
|
|
disabled={disabled}
|
|
autoComplete={autoComplete}
|
|
onBlur={onBlur}
|
|
/>
|
|
{isActionAvailable
|
|
&& <span className="input-group-append">
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary btn-icon btn-icon--green"
|
|
onClick={removeField}
|
|
>
|
|
<svg className="icon icon--24">
|
|
<use xlinkHref="#cross" />
|
|
</svg>
|
|
</button>
|
|
</span>
|
|
}
|
|
</div>
|
|
{!disabled && touched && error
|
|
&& <span className="form__message form__message--error"><Trans>{error}</Trans></span>}
|
|
</>
|
|
);
|
|
};
|
|
|
|
renderGroupField.propTypes = {
|
|
input: PropTypes.object.isRequired,
|
|
id: PropTypes.string,
|
|
className: PropTypes.string,
|
|
placeholder: PropTypes.string,
|
|
type: PropTypes.string,
|
|
disabled: PropTypes.bool,
|
|
autoComplete: PropTypes.bool,
|
|
isActionAvailable: PropTypes.bool,
|
|
removeField: PropTypes.func,
|
|
meta: PropTypes.shape({
|
|
touched: PropTypes.bool,
|
|
error: PropTypes.string,
|
|
}).isRequired,
|
|
normalizeOnBlur: PropTypes.func,
|
|
};
|
|
|
|
export const renderRadioField = ({
|
|
input,
|
|
placeholder,
|
|
subtitle,
|
|
disabled,
|
|
meta: { touched, error },
|
|
}) => <Fragment>
|
|
<label className="custom-control custom-radio">
|
|
<input {...input} type="radio" className="custom-control-input" disabled={disabled} />
|
|
<span className="custom-control-label">{placeholder}</span>
|
|
{subtitle && <span
|
|
className="checkbox__label-subtitle"
|
|
dangerouslySetInnerHTML={{ __html: subtitle }}
|
|
/>}
|
|
</label>
|
|
{!disabled
|
|
&& touched
|
|
&& error
|
|
&& <span className="form__message form__message--error"><Trans>{error}</Trans></span>}
|
|
</Fragment>;
|
|
|
|
renderRadioField.propTypes = {
|
|
input: PropTypes.object.isRequired,
|
|
placeholder: PropTypes.string,
|
|
subtitle: PropTypes.string,
|
|
disabled: PropTypes.bool,
|
|
meta: PropTypes.shape({
|
|
touched: PropTypes.bool,
|
|
error: PropTypes.string,
|
|
}).isRequired,
|
|
};
|
|
|
|
export const CheckboxField = ({
|
|
input,
|
|
placeholder,
|
|
subtitle,
|
|
disabled,
|
|
onClick,
|
|
modifier = 'checkbox--form',
|
|
meta: { touched, error },
|
|
}) => <>
|
|
<label className={`checkbox ${modifier}`} onClick={onClick}>
|
|
<span className="checkbox__marker" />
|
|
<input {...input} type="checkbox" className="checkbox__input" disabled={disabled} />
|
|
<span className="checkbox__label">
|
|
<span className="checkbox__label-text checkbox__label-text--long">
|
|
<span className="checkbox__label-title">{placeholder}</span>
|
|
{subtitle && <span className="checkbox__label-subtitle">{subtitle}</span>}
|
|
</span>
|
|
</span>
|
|
</label>
|
|
{!disabled
|
|
&& touched
|
|
&& error
|
|
&& <div className="form__message form__message--error mt-1"><Trans>{error}</Trans></div>}
|
|
</>;
|
|
|
|
CheckboxField.propTypes = {
|
|
input: PropTypes.object.isRequired,
|
|
placeholder: PropTypes.string,
|
|
subtitle: PropTypes.node,
|
|
disabled: PropTypes.bool,
|
|
onClick: PropTypes.func,
|
|
modifier: PropTypes.string,
|
|
checked: PropTypes.bool,
|
|
meta: PropTypes.shape({
|
|
touched: PropTypes.bool,
|
|
error: PropTypes.string,
|
|
}).isRequired,
|
|
};
|
|
|
|
export const renderSelectField = ({
|
|
input,
|
|
meta: { touched, error },
|
|
children,
|
|
label,
|
|
}) => {
|
|
const showWarning = touched && error;
|
|
|
|
return <>
|
|
{label && <label><Trans>{label}</Trans></label>}
|
|
<select {...input} className='form-control custom-select'>{children}</select>
|
|
{showWarning
|
|
&& <span className="form__message form__message--error form__message--left-pad"><Trans>{error}</Trans></span>}
|
|
</>;
|
|
};
|
|
|
|
renderSelectField.propTypes = {
|
|
input: PropTypes.object.isRequired,
|
|
disabled: PropTypes.bool,
|
|
label: PropTypes.string,
|
|
children: PropTypes.oneOfType([PropTypes.array, PropTypes.element]).isRequired,
|
|
meta: PropTypes.shape({
|
|
touched: PropTypes.bool,
|
|
error: PropTypes.string,
|
|
}).isRequired,
|
|
};
|
|
|
|
export const renderServiceField = ({
|
|
input,
|
|
placeholder,
|
|
disabled,
|
|
modifier,
|
|
icon,
|
|
meta: { touched, error },
|
|
}) => (
|
|
<>
|
|
<label className={cn('service custom-switch', { [modifier]: modifier })}>
|
|
<input
|
|
{...input}
|
|
type="checkbox"
|
|
className="custom-switch-input"
|
|
value={placeholder.toLowerCase()}
|
|
disabled={disabled}
|
|
/>
|
|
<span className="service__switch custom-switch-indicator"></span>
|
|
<span className="service__text" title={placeholder}>
|
|
{placeholder}
|
|
</span>
|
|
{icon && (
|
|
<div
|
|
dangerouslySetInnerHTML={{ __html: window.atob(icon) }}
|
|
className="service__icon"
|
|
/>
|
|
)}
|
|
</label>
|
|
{!disabled && touched && error && (
|
|
<span className="form__message form__message--error">
|
|
<Trans>{error}</Trans>
|
|
</span>
|
|
)}
|
|
</>
|
|
);
|
|
|
|
renderServiceField.propTypes = {
|
|
input: PropTypes.object.isRequired,
|
|
placeholder: PropTypes.string,
|
|
disabled: PropTypes.bool,
|
|
modifier: PropTypes.string,
|
|
icon: PropTypes.string,
|
|
meta: PropTypes.shape({
|
|
touched: PropTypes.bool,
|
|
error: PropTypes.string,
|
|
}).isRequired,
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @param {string} ip
|
|
* @returns {*}
|
|
*/
|
|
export const ip4ToInt = (ip) => {
|
|
const intIp = ip.split('.').reduce((int, oct) => (int * 256) + parseInt(oct, 10), 0);
|
|
return Number.isNaN(intIp) ? 0 : intIp;
|
|
};
|
|
|
|
/**
|
|
* @param value {string}
|
|
* @returns {*|number}
|
|
*/
|
|
export const toNumber = (value) => value && parseInt(value, 10);
|
|
|
|
/**
|
|
* @param value {string}
|
|
* @returns {*|number}
|
|
*/
|
|
export const toFloatNumber = (value) => value && parseFloat(value, 10);
|
|
|
|
/**
|
|
* @param value {string}
|
|
* @returns {boolean}
|
|
*/
|
|
export const isValidAbsolutePath = (value) => R_WIN_ABSOLUTE_PATH.test(value)
|
|
|| R_UNIX_ABSOLUTE_PATH.test(value);
|
|
|
|
/**
|
|
* @param value {string}
|
|
* @returns {*|string}
|
|
*/
|
|
export const normalizeMac = (value) => {
|
|
if (value && R_MAC_WITHOUT_COLON.test(value)) {
|
|
return value.match(/.{2}/g).join(':');
|
|
}
|
|
|
|
return value;
|
|
};
|