diff --git a/projects/packages/protect-models/changelog/update-protect-threats-data b/projects/packages/protect-models/changelog/update-protect-threats-data
new file mode 100644
index 0000000000000..1cb2d3079cb4d
--- /dev/null
+++ b/projects/packages/protect-models/changelog/update-protect-threats-data
@@ -0,0 +1,4 @@
+Significance: major
+Type: changed
+
+Changed the formatting of threat data.
diff --git a/projects/packages/protect-status/changelog/update-protect-threats-data b/projects/packages/protect-status/changelog/update-protect-threats-data
new file mode 100644
index 0000000000000..1cb2d3079cb4d
--- /dev/null
+++ b/projects/packages/protect-status/changelog/update-protect-threats-data
@@ -0,0 +1,4 @@
+Significance: major
+Type: changed
+
+Changed the formatting of threat data.
diff --git a/projects/plugins/protect/src/js/components/pricing-table/index.jsx b/projects/plugins/protect/src/js/components/pricing-table/index.jsx
index 3edd7911a0b6a..0f857430d92d8 100644
--- a/projects/plugins/protect/src/js/components/pricing-table/index.jsx
+++ b/projects/plugins/protect/src/js/components/pricing-table/index.jsx
@@ -11,9 +11,9 @@ import { __ } from '@wordpress/i18n';
import React, { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import useConnectSiteMutation from '../../data/use-connection-mutation';
+import useProductDataQuery from '../../data/use-product-data-query';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import usePlan from '../../hooks/use-plan';
-import useProtectData from '../../hooks/use-protect-data';
/**
* Product Detail component.
@@ -30,7 +30,7 @@ const ConnectedPricingTable = () => {
} );
// Access paid protect product data
- const { jetpackScan } = useProtectData();
+ const { data: jetpackScan } = useProductDataQuery();
const { pricingForUi } = jetpackScan;
const { introductoryOffer, currencyCode: currency = 'USD' } = pricingForUi;
diff --git a/projects/plugins/protect/src/js/hooks/use-protect-data/index.js b/projects/plugins/protect/src/js/hooks/use-protect-data/index.js
deleted file mode 100644
index 91daaf6a468e6..0000000000000
--- a/projects/plugins/protect/src/js/hooks/use-protect-data/index.js
+++ /dev/null
@@ -1,168 +0,0 @@
-import { __ } from '@wordpress/i18n';
-import { useMemo } from 'react';
-import useHistoryQuery from '../../data/scan/use-history-query';
-import useScanStatusQuery from '../../data/scan/use-scan-status-query';
-import useProductDataQuery from '../../data/use-product-data-query';
-
-// Valid "key" values for filtering.
-const KEY_FILTERS = [ 'all', 'core', 'plugins', 'themes', 'files', 'database' ];
-
-/**
- * Filter Extension Threats
- *
- * @param {Array} threats - The threats to filter.
- * @param {object} filter - The filter to apply to the data.
- * @param {string} filter.status - The status to filter: 'all', 'fixed', or 'ignored'.
- * @param {string} filter.key - The key to filter: 'all', 'core', 'files', 'database', or an extension name.
- * @param {string} key - The threat's key: 'all', 'core', 'files', 'database', or an extension name.
- *
- * @return {Array} The filtered threats.
- */
-const filterThreats = ( threats, filter, key ) => {
- if ( ! Array.isArray( threats ) ) {
- return [];
- }
-
- return threats.filter( threat => {
- if ( filter.status && filter.status !== 'all' && threat.status !== filter.status ) {
- return false;
- }
- if ( filter.key && filter.key !== 'all' && filter.key !== key ) {
- return false;
- }
- return true;
- } );
-};
-
-/**
- * Get parsed data from the initial state
- *
- * @param {object} options - The options to use when getting the data.
- * @param {string} options.sourceType - 'scan' or 'history'.
- * @param {object} options.filter - The filter to apply to the data.
- * _param {string} options.filter.status - 'all', 'fixed', or 'ignored'.
- * _param {string} options.filter.key - 'all', 'core', 'files', 'database', or an extension name.
- *
- * @return {object} The information available in Protect's initial state.
- */
-export default function useProtectData(
- { sourceType, filter } = {
- sourceType: 'scan',
- filter: { status: null, key: null },
- }
-) {
- const { data: status } = useScanStatusQuery();
- const { data: scanHistory } = useHistoryQuery();
- const { data: jetpackScan } = useProductDataQuery();
-
- const { counts, results, error, lastChecked, hasUncheckedItems } = useMemo( () => {
- // This hook can provide data from two sources: the current scan or the scan history.
- const data = sourceType === 'history' ? { ...scanHistory } : { ...status };
-
- // Prepare the result object.
- const result = {
- results: {
- core: [],
- plugins: [],
- themes: [],
- files: [],
- database: [],
- },
- counts: {
- all: {
- threats: 0,
- core: 0,
- plugins: 0,
- themes: 0,
- files: 0,
- database: 0,
- },
- current: {
- threats: 0,
- core: 0,
- plugins: 0,
- themes: 0,
- files: 0,
- database: 0,
- },
- },
- error: null,
- lastChecked: data.lastChecked || null,
- hasUncheckedItems: data.hasUncheckedItems || false,
- };
-
- // Loop through the provided extensions, and update the result object.
- const processExtensions = ( extensions, key ) => {
- if ( ! Array.isArray( extensions ) ) {
- return [];
- }
- extensions.forEach( extension => {
- // Update the total counts.
- result.counts.all[ key ] += extension?.threats?.length || 0;
- result.counts.all.threats += extension?.threats?.length || 0;
-
- // Filter the extension's threats based on the current filters.
- const filteredThreats = filterThreats(
- extension?.threats,
- filter,
- KEY_FILTERS.includes( filter.key ) ? key : extension?.name
- );
-
- // Update the result object with the extension and its filtered threats.
- result.results[ key ].push( { ...extension, threats: filteredThreats } );
-
- // Update the current counts.
- result.counts.current[ key ] += filteredThreats.length;
- result.counts.current.threats += filteredThreats.length;
- } );
- };
-
- // Loop through the provided threats, and update the result object.
- const processThreats = ( threatsToProcess, key ) => {
- if ( ! Array.isArray( threatsToProcess ) ) {
- return [];
- }
-
- result.counts.all[ key ] += threatsToProcess.length;
- result.counts.all.threats += threatsToProcess.length;
-
- const filteredThreats = filterThreats( threatsToProcess, filter, key );
-
- result.results[ key ] = [ ...result.results[ key ], ...filteredThreats ];
- result.counts.current[ key ] += filteredThreats.length;
- result.counts.current.threats += filteredThreats.length;
- };
-
- // Core data may be either a single object or an array of multiple objects.
- let cores = Array.isArray( data.core ) ? data.core : [];
- if ( data.core.threats ) {
- cores = [ data.core ];
- }
-
- // Process the data
- processExtensions( cores, 'core' );
- processExtensions( data?.plugins, 'plugins' );
- processExtensions( data?.themes, 'themes' );
- processThreats( data?.files, 'files' );
- processThreats( data?.database, 'database' );
-
- // Handle errors
- if ( data.error ) {
- result.error = {
- message: data.errorMessage || __( 'An error occurred.', 'jetpack-protect' ),
- code: data.errorCode || 500,
- };
- }
-
- return result;
- }, [ scanHistory, sourceType, status, filter ] );
-
- return {
- results,
- counts,
- error,
- lastChecked,
- hasUncheckedItems,
- jetpackScan,
- };
-}
diff --git a/projects/plugins/protect/src/js/routes/scan/history/index.jsx b/projects/plugins/protect/src/js/routes/scan/history/index.jsx
index b9fac81d88775..42c5f32a8b6de 100644
--- a/projects/plugins/protect/src/js/routes/scan/history/index.jsx
+++ b/projects/plugins/protect/src/js/routes/scan/history/index.jsx
@@ -1,16 +1,13 @@
-import { AdminSectionHero, Container, Col, H3, Text, Title } from '@automattic/jetpack-components';
-import { __, _n, sprintf } from '@wordpress/i18n';
-import { useCallback } from 'react';
+import { AdminSectionHero, Container, Col, H3, Text } from '@automattic/jetpack-components';
+import { __, sprintf } from '@wordpress/i18n';
import { Navigate, useParams } from 'react-router-dom';
import AdminPage from '../../../components/admin-page';
import ErrorScreen from '../../../components/error-section';
import ProtectCheck from '../../../components/protect-check-icon';
import ScanFooter from '../../../components/scan-footer';
-import ThreatsNavigation from '../../../components/threats-list/navigation';
-import useThreatsList from '../../../components/threats-list/use-threats-list';
+import useHistoryQuery from '../../../data/scan/use-history-query';
import useAnalyticsTracks from '../../../hooks/use-analytics-tracks';
import usePlan from '../../../hooks/use-plan';
-import useProtectData from '../../../hooks/use-protect-data';
import ScanSectionHeader from '../scan-section-header';
import ScanHistoryDataView from './scan-history-data-view';
import styles from './styles.module.scss';
@@ -22,276 +19,64 @@ const ScanHistoryRoute = () => {
const { hasPlan } = usePlan();
const { filter = 'all' } = useParams();
- const { item, list, selected, setSelected } = useThreatsList( {
- source: 'history',
- status: filter,
- } );
-
- const { counts, error } = useProtectData( {
- sourceType: 'history',
- filter: { status: filter },
- } );
- const { threats: numAllThreats } = counts.all;
-
- /**
- * Get the title for the threats list based on the selected filters and the amount of threats.
- */
- const getTitle = useCallback( () => {
- switch ( selected ) {
- case 'all':
- if ( list.length === 1 ) {
- switch ( filter ) {
- case 'fixed':
- return __( 'All fixed threats', 'jetpack-protect' );
- case 'ignored':
- return __(
- 'All ignored threats',
- 'jetpack-protect',
- /** dummy arg to avoid bad minification */ 0
- );
- default:
- return __( 'All threats', 'jetpack-protect' );
- }
- }
- switch ( filter ) {
- case 'fixed':
- return sprintf(
- /* translators: placeholder is the amount of fixed threats found on the site. */
- __( 'All %s fixed threats', 'jetpack-protect' ),
- list.length
- );
- case 'ignored':
- return sprintf(
- /* translators: placeholder is the amount of ignored threats found on the site. */
- __( 'All %s ignored threats', 'jetpack-protect' ),
- list.length
- );
- default:
- return sprintf(
- /* translators: placeholder is the amount of threats found on the site. */
- __( 'All %s threats', 'jetpack-protect' ),
- list.length
- );
- }
- case 'core':
- switch ( filter ) {
- case 'fixed':
- return sprintf(
- /* translators: placeholder is the amount of fixed WordPress threats found on the site. */
- _n(
- '%1$s fixed WordPress threat',
- '%1$s fixed WordPress threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- case 'ignored':
- return sprintf(
- /* translators: placeholder is the amount of ignored WordPress threats found on the site. */
- _n(
- '%1$s ignored WordPress threat',
- '%1$s ignored WordPress threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- default:
- return sprintf(
- /* translators: placeholder is the amount of WordPress threats found on the site. */
- _n(
- '%1$s WordPress threat',
- '%1$s WordPress threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- }
- case 'files':
- switch ( filter ) {
- case 'fixed':
- return sprintf(
- /* translators: placeholder is the amount of fixed file threats found on the site. */
- _n(
- '%1$s fixed file threat',
- '%1$s fixed file threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- case 'ignored':
- return sprintf(
- /* translators: placeholder is the amount of ignored file threats found on the site. */
- _n(
- '%1$s ignored file threat',
- '%1$s ignored file threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- default:
- return sprintf(
- /* translators: placeholder is the amount of file threats found on the site. */
- _n( '%1$s file threat', '%1$s file threats', list.length, 'jetpack-protect' ),
- list.length
- );
- }
- case 'database':
- switch ( filter ) {
- case 'fixed':
- return sprintf(
- /* translators: placeholder is the amount of fixed database threats found on the site. */
- _n(
- '%1$s fixed database threat',
- '%1$s fixed database threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- case 'ignored':
- return sprintf(
- /* translators: placeholder is the amount of ignored database threats found on the site. */
- _n(
- '%1$s ignored database threat',
- '%1$s ignored database threats',
- list.length,
- 'jetpack-protect'
- ),
- list.length
- );
- default:
- return sprintf(
- /* translators: placeholder is the amount of database threats found on the site. */
- _n( '%1$s database threat', '%1$s database threats', list.length, 'jetpack-protect' ),
- list.length
- );
- }
- default:
- switch ( filter ) {
- case 'fixed':
- return sprintf(
- /* translators: Translates to "123 fixed threats in Example Plugin (1.2.3)" */
- _n(
- '%1$s fixed threat in %2$s %3$s',
- '%1$s fixed threats in %2$s %3$s',
- list.length,
- 'jetpack-protect'
- ),
- list.length,
- item?.name,
- item?.version
- );
- case 'ignored':
- return sprintf(
- /* translators: Translates to "123 ignored threats in Example Plugin (1.2.3)" */
- _n(
- '%1$s ignored threat in %2$s %3$s',
- '%1$s ignored threats in %2$s %3$s',
- list.length,
- 'jetpack-protect'
- ),
- list.length,
- item?.name,
- item?.version
- );
- default:
- return sprintf(
- /* translators: Translates to "123 threats in Example Plugin (1.2.3)" */
- _n(
- '%1$s threat in %2$s %3$s',
- '%1$s threats in %2$s %3$s',
- list.length,
- 'jetpack-protect'
- ),
- list.length,
- item?.name,
- item?.version
- );
- }
- }
- }, [ selected, list.length, filter, item?.name, item?.version ] );
+ const { data: status } = useHistoryQuery();
// Threat history is only available for paid plans.
if ( ! hasPlan ) {
return ;
}
- // Remove the filter if there are no threats to show.
- if ( list.length === 0 && filter !== 'all' ) {
- return ;
- }
-
return (
- { error ? (
+ { status.error ? (
) : (
-
-
-
-
-
- { list.length > 0 ? (
-
- ) : (
-
-
-
- { __( "Don't worry about a thing", 'jetpack-protect' ) }
-
-
- { sprintf(
- /* translators: %s: Filter type */
- __( 'There are no%sthreats in your scan history.', 'jetpack-protect' ),
- 'all' === filter ? ' ' : ` ${ filter } `
- ) }
-
-
- ) }
-
-
+ { status.threats.length > 0 ? (
+
+
+
+ ) : (
+
+
+
+ { __( "Don't worry about a thing", 'jetpack-protect' ) }
+
+
+ { sprintf(
+ /* translators: %s: Filter type */
+ __( 'There are no%sthreats in your scan history.', 'jetpack-protect' ),
+ 'all' === filter ? ' ' : ` ${ filter } `
+ ) }
+
+
+ ) }
) }
diff --git a/projects/plugins/protect/src/js/routes/scan/index.jsx b/projects/plugins/protect/src/js/routes/scan/index.jsx
index 3f49e4ff55151..eb6b790e82280 100644
--- a/projects/plugins/protect/src/js/routes/scan/index.jsx
+++ b/projects/plugins/protect/src/js/routes/scan/index.jsx
@@ -12,7 +12,6 @@ import useScanStatusQuery, { isScanInProgress } from '../../data/scan/use-scan-s
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import { OnboardingContext } from '../../hooks/use-onboarding';
import usePlan from '../../hooks/use-plan';
-import useProtectData from '../../hooks/use-protect-data';
import useWafData from '../../hooks/use-waf-data';
import onboardingSteps from './onboarding-steps';
import ScanResultsDataView from './scan-results-data-view';
@@ -123,13 +122,12 @@ const ScanInProgressSection = ( { currentProgress } ) => {
*/
const ScanPage = () => {
const { hasPlan } = usePlan();
- const { lastChecked } = useProtectData();
const { data: status } = useScanStatusQuery( { usePolling: true } );
let currentScanStatus;
if ( status.error ) {
currentScanStatus = 'error';
- } else if ( ! lastChecked ) {
+ } else if ( ! status.lastChecked ) {
currentScanStatus = 'in_progress';
} else {
currentScanStatus = 'active';