- client: Fix query logs UI issues

Close #1828

Squashed commit of the following:

commit a3955c989a939866c6772b147547344b3f8769c4
Merge: c91c41cb 2759d81a
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 15:14:47 2020 +0300

    Merge branch 'master' into fix/1828

commit c91c41cbc5f616e0af1092424e42b909d2f43f7c
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 13:48:54 2020 +0300

    Fix cell overflow

commit 19e1d31a40f2e1bb1189a85b72507bcc364d4e0c
Merge: af31f48c a33164bf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 12:36:44 2020 +0300

    Merge branch 'master' into fix/1828

commit af31f48c4d2699ebfbd2034711c51499b42e40f5
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 10:45:57 2020 +0300

    minor

commit d9507c5f3f5758e587766ae0fa45f1b9ad703ccf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 18:34:22 2020 +0300

    - client: Fix query logs UI issues
This commit is contained in:
Artem Baskal 2020-07-13 15:23:13 +03:00
parent 2759d81afe
commit 8a417604a9
9 changed files with 76 additions and 51 deletions

View File

@ -480,6 +480,7 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts lists.",
"blocked_by_response": "Blocked by CNAME or IP in response",
"blocked_by_cname_or_ip": "Blocked by CNAME or IP",
"try_again": "Try again",
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
"example_rewrite_domain": "rewrite responses for this domain name only.",
@ -560,5 +561,6 @@
"filter_category_general_desc": "Lists that block tracking and advertising on most of the devices",
"filter_category_security_desc": "Lists that specialize on blocking malware, phishing or scam domains",
"filter_category_regional_desc": "Lists that focus on regional ads and tracking servers",
"filter_category_other_desc": "Other blocklists"
"filter_category_other_desc": "Other blocklists",
"original_response": "Original response"
}

View File

@ -68,7 +68,7 @@ const getClientCell = ({
return (
<div className="logs__row o-hidden h-100">
{processedData && getHintElement({
{getHintElement({
className: hintClass,
columnClass: 'grid grid--limited',
tooltipClass: 'px-5 pb-5 pt-4 mw-75',

View File

@ -33,16 +33,6 @@ const getDomainCell = (props) => {
'my-3': isDetailed,
});
const dnssecHint = getHintElement({
className: lockIconClass,
tooltipClass: 'py-4 px-5 pb-45',
canShowTooltip: answer_dnssec,
xlinkHref: 'lock',
columnClass: 'w-100',
content: 'validated_with_dnssec',
placement: 'bottom',
});
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
const ip = type ? `${t('type_table_header')}: ${type}` : '';
@ -100,7 +90,15 @@ const getDomainCell = (props) => {
return (
<div className="logs__row o-hidden">
{dnssec_enabled && dnssecHint}
{dnssec_enabled && getHintElement({
className: lockIconClass,
tooltipClass: 'py-4 px-5 pb-45',
canShowTooltip: answer_dnssec,
xlinkHref: 'lock',
columnClass: 'w-100',
content: 'validated_with_dnssec',
placement: 'bottom',
})}
{trackerHint}
<div className={valueClass}>
<div className="text-truncate" title={domain}>{domain}</div>

View File

@ -6,6 +6,7 @@ import classNames from 'classnames';
import './Tooltip.css';
import 'react-popper-tooltip/dist/styles.css';
import { HIDE_TOOLTIP_DELAY } from '../../../helpers/constants';
import { processContent } from '../../../helpers/helpers';
const getHintElement = ({
className,
@ -17,12 +18,12 @@ const getHintElement = ({
placement,
tooltipClass,
content,
renderContent = React.Children.map(
content,
renderContent = content ? React.Children.map(
processContent(content),
(item, idx) => <div key={idx} className={contentItemClass}>
<Trans>{item || '—'}</Trans>
</div>,
),
) : null,
}) => <TooltipTrigger placement={placement} trigger="hover" delayHide={HIDE_TOOLTIP_DELAY} tooltip={
({
tooltipRef,
@ -37,14 +38,14 @@ const getHintElement = ({
</div>}
<div className={classNames(columnClass)}>{renderContent}</div>
</div>
}>{({
}>{({
getTriggerProps, triggerRef,
}) => <span {...getTriggerProps({ ref: triggerRef })}>
}) => <span {...getTriggerProps({ ref: triggerRef })}>
{xlinkHref && <svg className={className}>
<use xlinkHref={`#${xlinkHref}`} />
</svg>}
</span>}
</TooltipTrigger>;
</TooltipTrigger>;
getHintElement.propTypes = {
className: PropTypes.string,

View File

@ -9,18 +9,24 @@ import getHintElement from './getHintElement';
const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
const {
reason, filterId, rule, status, upstream, elapsedMs, domain, response,
reason, filterId, rule, status, upstream, elapsedMs,
domain, response, originalResponse,
} = row.original;
const { filters, whitelistFilters } = filtering;
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
const statusLabel = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const boldStatusLabel = <span className="font-weight-bold">{statusLabel}</span>;
const filter = getFilterName(filters, whitelistFilters, filterId, t);
const renderResponses = (responseArr) => {
if (responseArr.length === 0) {
if (responseArr?.length === 0) {
return '';
}
@ -50,6 +56,7 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
filter,
rule_label: rule,
response_code: status,
original_response: renderResponses(originalResponse),
},
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
domain,
@ -78,9 +85,11 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
domain,
encryption_status: boldStatusLabel,
filter,
rule_label: rule,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
response_code: status,
original_response: renderResponses(originalResponse),
},
};
@ -88,13 +97,11 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason])
: Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound);
const detailedInfo = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE
|| reason === FILTERED_STATUS.FILTERED_BLACK_LIST
? filter : formattedElapsedMs;
const detailedInfo = isBlocked ? filter : formattedElapsedMs;
return (
<div className="logs__row">
{fields && getHintElement({
{getHintElement({
className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }),
columnClass: 'grid grid--limited',
tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details',
@ -107,7 +114,8 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
<div className="text-truncate">
<div className="text-truncate" title={statusLabel}>{statusLabel}</div>
{isDetailed && <div
className="detailed-info d-none d-sm-block pt-1 text-truncate" title={detailedInfo}>{detailedInfo}</div>}
className="detailed-info d-none d-sm-block pt-1 text-truncate"
title={detailedInfo}>{detailedInfo}</div>}
</div>
</div>
);

View File

@ -12,7 +12,7 @@ import {
FILTERED_STATUS_TO_META_MAP,
TABLE_DEFAULT_PAGE_SIZE,
SCHEME_TO_PROTOCOL_MAP,
CUSTOM_FILTERING_RULES_ID,
CUSTOM_FILTERING_RULES_ID, FILTERED_STATUS,
} from '../../helpers/constants';
import getDateCell from './Cells/getDateCell';
import getDomainCell from './Cells/getDomainCell';
@ -300,6 +300,8 @@ const Table = (props) => {
type,
client_proto,
filterId,
rule,
originalResponse,
} = rowInfo.original;
const hasTracker = !!tracker;
@ -317,12 +319,16 @@ const Table = (props) => {
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
const isFiltered = checkFiltered(reason);
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
const onToggleBlock = () => {
toggleBlocking(buttonType, domain);
};
const status = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
const status = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const statusBlocked = <div className="bg--danger">{status}</div>;
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
@ -379,12 +385,14 @@ const Table = (props) => {
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
filter,
rule_label: rule,
response_table_header: response?.join('\n'),
original_response: originalResponse?.join('\n'),
[buttonType]: <div onClick={onToggleBlock}
className="title--border text-center">{t(buttonType)}</div>,
};
const detailedDataCurrent = isFiltered ? detailedDataBlocked : detailedData;
const detailedDataCurrent = isBlocked ? detailedDataBlocked : detailedData;
setDetailedDataCurrent(detailedDataCurrent);
setButtonType(buttonType);

View File

@ -24,6 +24,10 @@ const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST];
export const processContent = (data, buttonType) => Object.entries(data)
.map(([key, value]) => {
if (!value) {
return null;
}
const isTitle = value === 'title';
const isButton = key === buttonType;
const isBoolean = typeof value === 'boolean';

View File

@ -52,7 +52,7 @@ export const formatClientCell = (row, t, isDetailed = false) => {
}
return (
<div className="logs__text" title={value}>
<div className="logs__text mw-100" title={value}>
<>
{nameContainer}
{whoisContainer}

View File

@ -86,18 +86,16 @@ export const normalizeLogs = (logs) => logs.map((log) => {
const { host: domain, type } = question;
const response = answer ? answer.map((response) => {
const processResponse = (data) => (data ? data.map((response) => {
const { value, type, ttl } = response;
return `${type}: ${value} (ttl=${ttl})`;
}) : [];
const tracker = getTrackerData(domain);
}) : []);
return {
time,
domain,
type,
response,
response: processResponse(answer),
reason,
client,
client_proto,
@ -106,7 +104,8 @@ export const normalizeLogs = (logs) => logs.map((log) => {
status,
serviceName: service_name,
originalAnswer: original_answer,
tracker,
originalResponse: processResponse(original_answer),
tracker: getTrackerData(domain),
answer_dnssec,
elapsedMs,
upstream,
@ -618,3 +617,8 @@ export const selectCompletedFields = (values) => Object.entries(values)
}
return acc;
}, {});
export const processContent = (content) => (Array.isArray(content)
? content.filter(([, value]) => value)
.flat() : content);