diff --git a/.githooks/pre-commit b/.githooks/pre-commit index d933e462..4fd4e4f5 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,6 +1,10 @@ #!/bin/bash set -e; -git diff --cached --name-only | grep -q '.js$' && make lint-js; +git diff --cached --name-only | grep -q '.js$' && found=1 +if [ $found == 1 ]; then + make lint-js || exit 1 + npm run test --prefix client || exit 1 +fi found=0 git diff --cached --name-only | grep -q '.go$' && found=1 diff --git a/client/package-lock.json b/client/package-lock.json index 84c8e186..42cac10f 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12377,9 +12377,9 @@ } }, "react-i18next": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.4.0.tgz", - "integrity": "sha512-lyOZSSQkif4H9HnHN3iEKVkryLI+WkdZSEw3VAZzinZLopfYRMHVY5YxCopdkXPLEHs6S5GjKYPh3+j0j336Fg==", + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.7.2.tgz", + "integrity": "sha512-Djj3K3hh5Tecla2CI9rLO3TZBYGMFrGilm0JY4cLofAQONCi5TK6nVmUPKoB59n1ZffgjfgJt6zlbE9aGF6Q0Q==", "requires": { "@babel/runtime": "^7.3.1", "html-parse-stringify2": "2.0.1" diff --git a/client/package.json b/client/package.json index fdc19c9d..4ad8d5eb 100644 --- a/client/package.json +++ b/client/package.json @@ -28,7 +28,7 @@ "react": "^16.13.1", "react-click-outside": "^3.0.1", "react-dom": "^16.13.1", - "react-i18next": "^11.4.0", + "react-i18next": "^11.7.2", "react-modal": "^3.11.2", "react-popper-tooltip": "^2.11.1", "react-redux": "^7.2.0", diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 6abd1d4c..a3514327 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -366,7 +366,7 @@ "fix": "Fix", "dns_providers": "Here is a <0>list of known DNS providers to choose from.", "update_now": "Update now", - "update_failed": "Auto-update failed. Please follow the steps to update manually.", + "update_failed": "Auto-update failed. Please follow these steps to update manually.", "processing_update": "Please wait, AdGuard Home is being updated", "clients_title": "Clients", "clients_desc": "Configure devices connected to AdGuard Home", diff --git a/client/src/__tests__/helpers.test.js b/client/src/__tests__/helpers.test.js index f974cca6..bb371be4 100644 --- a/client/src/__tests__/helpers.test.js +++ b/client/src/__tests__/helpers.test.js @@ -1,5 +1,7 @@ -import { getIpMatchListStatus, sortIp } from '../helpers/helpers'; -import { IP_MATCH_LIST_STATUS } from '../helpers/constants'; +import { + countClientsStatistics, findAddressType, getIpMatchListStatus, sortIp, +} from '../helpers/helpers'; +import { ADDRESS_TYPES, IP_MATCH_LIST_STATUS } from '../helpers/constants'; describe('getIpMatchListStatus', () => { describe('IPv4', () => { @@ -482,3 +484,56 @@ describe('sortIp', () => { }); }); }); + +describe('findAddressType', () => { + describe('ip', () => { + expect(findAddressType('127.0.0.1')).toStrictEqual(ADDRESS_TYPES.IP); + }); + describe('cidr', () => { + expect(findAddressType('127.0.0.1/8')).toStrictEqual(ADDRESS_TYPES.CIDR); + }); + describe('mac', () => { + expect(findAddressType('00:1B:44:11:3A:B7')).toStrictEqual(ADDRESS_TYPES.UNKNOWN); + }); +}); + +describe('countClientsStatistics', () => { + test('single ip', () => { + expect(countClientsStatistics(['127.0.0.1'], { + '127.0.0.1': 1, + })).toStrictEqual(1); + }); + test('multiple ip', () => { + expect(countClientsStatistics(['127.0.0.1', '127.0.0.2'], { + '127.0.0.1': 1, + '127.0.0.2': 2, + })).toStrictEqual(1 + 2); + }); + test('cidr', () => { + expect(countClientsStatistics(['127.0.0.0/8'], { + '127.0.0.1': 1, + '127.0.0.2': 2, + })).toStrictEqual(1 + 2); + }); + test('cidr and multiple ip', () => { + expect(countClientsStatistics(['1.1.1.1', '2.2.2.2', '3.3.3.0/24'], { + '1.1.1.1': 1, + '2.2.2.2': 2, + '3.3.3.3': 3, + })).toStrictEqual(1 + 2 + 3); + }); + test('mac', () => { + expect(countClientsStatistics(['00:1B:44:11:3A:B7', '2.2.2.2', '3.3.3.0/24'], { + '1.1.1.1': 1, + '2.2.2.2': 2, + '3.3.3.3': 3, + })).toStrictEqual(2 + 3); + }); + test('not found', () => { + expect(countClientsStatistics(['4.4.4.4', '5.5.5.5', '6.6.6.6'], { + '1.1.1.1': 1, + '2.2.2.2': 2, + '3.3.3.3': 3, + })).toStrictEqual(0); + }); +}); diff --git a/client/src/actions/index.js b/client/src/actions/index.js index d4018bb0..ff039af1 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -4,9 +4,10 @@ import axios from 'axios'; import endsWith from 'lodash/endsWith'; import escapeRegExp from 'lodash/escapeRegExp'; +import React from 'react'; import { splitByNewLine, sortClients } from '../helpers/helpers'; import { - BLOCK_ACTIONS, CHECK_TIMEOUT, STATUS_RESPONSE, SETTINGS_NAMES, FORM_NAME, + BLOCK_ACTIONS, CHECK_TIMEOUT, STATUS_RESPONSE, SETTINGS_NAMES, FORM_NAME, GETTING_STARTED_LINK, } from '../helpers/constants'; import { areEqualVersions } from '../helpers/version'; import { getTlsStatus } from './encryption'; @@ -184,7 +185,14 @@ export const getUpdate = () => async (dispatch, getState) => { dispatch(getUpdateRequest()); const handleRequestError = () => { - dispatch(addNoticeToast({ error: 'update_failed' })); + const options = { + components: { + a: , + }, + }; + + dispatch(addNoticeToast({ error: 'update_failed', options })); dispatch(getUpdateFailure()); }; diff --git a/client/src/components/Logs/Logs.css b/client/src/components/Logs/Logs.css index fd088b79..b230ab8b 100644 --- a/client/src/components/Logs/Logs.css +++ b/client/src/components/Logs/Logs.css @@ -388,3 +388,28 @@ .logs__table .loading:before { min-height: 100%; } + +.logs__whois { + display: inline; + font-size: 12px; + white-space: nowrap; +} + +.logs__whois::after { + content: "|"; + padding: 0 5px; + opacity: 0.3; +} + +.logs__whois:last-child::after { + content: ""; +} + +.logs__whois-icon.icons { + position: relative; + top: -2px; + width: 12px; + height: 12px; + margin-right: 1px; + opacity: 0.5; +} diff --git a/client/src/components/Settings/Clients/ClientsTable.js b/client/src/components/Settings/Clients/ClientsTable.js index cb7f2914..ac613164 100644 --- a/client/src/components/Settings/Clients/ClientsTable.js +++ b/client/src/components/Settings/Clients/ClientsTable.js @@ -4,7 +4,7 @@ import { Trans, withTranslation } from 'react-i18next'; import ReactTable from 'react-table'; import { MODAL_TYPE } from '../../../helpers/constants'; -import { splitByNewLine } from '../../../helpers/helpers'; +import { splitByNewLine, countClientsStatistics } from '../../../helpers/helpers'; import Card from '../../ui/Card'; import Modal from './Modal'; import CellWrap from '../../ui/CellWrap'; @@ -204,7 +204,10 @@ class ClientsTable extends Component { { Header: this.props.t('requests_count'), id: 'statistics', - accessor: (row) => this.props.normalizedTopClients.configured[row.name] || 0, + accessor: (row) => countClientsStatistics( + row.ids, + this.props.normalizedTopClients.auto, + ), sortMethod: (a, b) => b - a, minWidth: 120, Cell: (row) => { diff --git a/client/src/components/Settings/Clients/whoisCell.js b/client/src/components/Settings/Clients/whoisCell.js index 94afd6cc..1a8b0484 100644 --- a/client/src/components/Settings/Clients/whoisCell.js +++ b/client/src/components/Settings/Clients/whoisCell.js @@ -14,7 +14,7 @@ const getFormattedWhois = (value, t) => {
{icon && ( - +   diff --git a/client/src/components/Toasts/Toast.js b/client/src/components/Toasts/Toast.js index a4c58aad..4c46078a 100644 --- a/client/src/components/Toasts/Toast.js +++ b/client/src/components/Toasts/Toast.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { useTranslation } from 'react-i18next'; +import { Trans } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { TOAST_TIMEOUTS } from '../../helpers/constants'; import { removeToast } from '../../actions'; @@ -9,8 +9,8 @@ const Toast = ({ id, message, type, + options, }) => { - const { t } = useTranslation(); const dispatch = useDispatch(); const [timerId, setTimerId] = useState(null); @@ -30,7 +30,12 @@ const Toast = ({ return
-

{t(message)}

+

+ +