From 66ac702e1cf4ca9e674cbc4a18567fc36180d9cb Mon Sep 17 00:00:00 2001 From: Nate Weller Date: Tue, 30 Jul 2024 21:10:23 -0600 Subject: [PATCH] Refactor useProtectData hook Fix extraction of count values in scan history route Include extensions with no threats in useProtectData hook Init feature branch Remove unused component prop Fixup project versions Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Rename add-protect-threat-history-hooks-refactor to add-protect-threat-history Remove the status filter when there are no results to show Prevent access to history routes when user has no plan Remove isFetching check from scanError selector Remove optimistic scan status check from scanError selector Adjustments to scan fetching/in progress/results/error state management Fix status.status Adjust scan status selectors --- .../changelog/add-protect-threat-history | 5 + projects/plugins/backup/composer.lock | 4 +- .../changelog/add-protect-threat-history | 5 + projects/plugins/boost/composer.lock | 4 +- .../changelog/add-protect-threat-history | 5 + projects/plugins/jetpack/composer.lock | 4 +- .../changelog/add-protect-threat-history | 5 + projects/plugins/migration/composer.lock | 4 +- .../js/components/paid-plan-gate/index.tsx | 29 +++ .../src/js/components/summary/index.jsx | 8 +- .../src/js/components/threats-list/index.jsx | 2 +- .../js/components/threats-list/navigation.jsx | 19 +- .../js/components/threats-list/paid-list.jsx | 3 +- .../threats-list/use-threats-list.js | 16 +- .../src/js/hooks/use-protect-data/index.js | 217 ++++++++++++------ projects/plugins/protect/src/js/index.tsx | 19 +- .../src/js/routes/scan/history/index.jsx | 39 +++- .../js/routes/scan/history/status-filters.jsx | 8 +- .../plugins/protect/src/js/state/selectors.js | 33 +-- .../changelog/add-protect-threat-history | 5 + projects/plugins/search/composer.lock | 4 +- .../changelog/add-protect-threat-history | 5 + projects/plugins/social/composer.lock | 4 +- .../changelog/add-protect-threat-history | 5 + projects/plugins/starter-plugin/composer.lock | 4 +- .../changelog/add-protect-threat-history | 5 + projects/plugins/videopress/composer.lock | 4 +- 27 files changed, 328 insertions(+), 137 deletions(-) create mode 100644 projects/plugins/backup/changelog/add-protect-threat-history create mode 100644 projects/plugins/boost/changelog/add-protect-threat-history create mode 100644 projects/plugins/jetpack/changelog/add-protect-threat-history create mode 100644 projects/plugins/migration/changelog/add-protect-threat-history create mode 100644 projects/plugins/protect/src/js/components/paid-plan-gate/index.tsx create mode 100644 projects/plugins/search/changelog/add-protect-threat-history create mode 100644 projects/plugins/social/changelog/add-protect-threat-history create mode 100644 projects/plugins/starter-plugin/changelog/add-protect-threat-history create mode 100644 projects/plugins/videopress/changelog/add-protect-threat-history diff --git a/projects/plugins/backup/changelog/add-protect-threat-history b/projects/plugins/backup/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/backup/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/backup/composer.lock b/projects/plugins/backup/composer.lock index 7751d25402a9f..1a6091fba6c83 100644 --- a/projects/plugins/backup/composer.lock +++ b/projects/plugins/backup/composer.lock @@ -1438,7 +1438,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1455,7 +1455,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/boost/changelog/add-protect-threat-history b/projects/plugins/boost/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/boost/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/boost/composer.lock b/projects/plugins/boost/composer.lock index 44436439f5b39..bd75d8f10504a 100644 --- a/projects/plugins/boost/composer.lock +++ b/projects/plugins/boost/composer.lock @@ -1422,7 +1422,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1439,7 +1439,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/jetpack/changelog/add-protect-threat-history b/projects/plugins/jetpack/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..a1c1831fa1ef7 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Updated composer.lock. + + diff --git a/projects/plugins/jetpack/composer.lock b/projects/plugins/jetpack/composer.lock index 60e3a5c4c59b4..e587be715e38a 100644 --- a/projects/plugins/jetpack/composer.lock +++ b/projects/plugins/jetpack/composer.lock @@ -2084,7 +2084,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -2101,7 +2101,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/migration/changelog/add-protect-threat-history b/projects/plugins/migration/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/migration/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/migration/composer.lock b/projects/plugins/migration/composer.lock index 31f733c347bd5..9fcff5fda9d83 100644 --- a/projects/plugins/migration/composer.lock +++ b/projects/plugins/migration/composer.lock @@ -1438,7 +1438,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1455,7 +1455,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/protect/src/js/components/paid-plan-gate/index.tsx b/projects/plugins/protect/src/js/components/paid-plan-gate/index.tsx new file mode 100644 index 0000000000000..e6a521675198b --- /dev/null +++ b/projects/plugins/protect/src/js/components/paid-plan-gate/index.tsx @@ -0,0 +1,29 @@ +import { Navigate } from 'react-router-dom'; +import useProtectData from '../../hooks/use-protect-data'; + +/** + * Paid Plan Gate + * + * Custom route that only renders when the user has a paid plan. + * + * @param {object} props - The component props. + * @param {JSX.Element} props.children - The component to render if the user has a paid plan. + * @param {string} props.redirect - The alternate route to redirect to if the user does not have a paid plan. + * + * @returns {JSX.Element} The PaidPlanRoute component. + */ +export default function PaidPlanGate( { + children, + redirect = '/', +}: { + children?: JSX.Element; + redirect?: string; +} ): JSX.Element { + const { hasRequiredPlan } = useProtectData(); + + if ( ! hasRequiredPlan ) { + return ; + } + + return children; +} diff --git a/projects/plugins/protect/src/js/components/summary/index.jsx b/projects/plugins/protect/src/js/components/summary/index.jsx index d9a0d3623e57a..60abb79dba7a3 100644 --- a/projects/plugins/protect/src/js/components/summary/index.jsx +++ b/projects/plugins/protect/src/js/components/summary/index.jsx @@ -8,7 +8,13 @@ import OnboardingPopover from '../onboarding-popover'; const Summary = () => { const [ isSm ] = useBreakpointMatch( 'sm' ); - const { numThreats, lastChecked, hasRequiredPlan } = useProtectData(); + const { + counts: { + current: { threats: numThreats }, + }, + lastChecked, + hasRequiredPlan, + } = useProtectData(); // Popover anchors const [ dailyScansPopoverAnchor, setDailyScansPopoverAnchor ] = useState( null ); diff --git a/projects/plugins/protect/src/js/components/threats-list/index.jsx b/projects/plugins/protect/src/js/components/threats-list/index.jsx index d984fea2ce81d..f86f77e463c06 100644 --- a/projects/plugins/protect/src/js/components/threats-list/index.jsx +++ b/projects/plugins/protect/src/js/components/threats-list/index.jsx @@ -58,7 +58,7 @@ const ThreatsList = () => { __( 'All %s threats', 'jetpack-protect' ), list.length ); - case 'wordpress': + case 'core': return sprintf( /* translators: placeholder is the amount of WordPress threats found on the site. */ __( '%1$s WordPress %2$s', 'jetpack-protect' ), diff --git a/projects/plugins/protect/src/js/components/threats-list/navigation.jsx b/projects/plugins/protect/src/js/components/threats-list/navigation.jsx index df237b77df28e..dbab3a326703d 100644 --- a/projects/plugins/protect/src/js/components/threats-list/navigation.jsx +++ b/projects/plugins/protect/src/js/components/threats-list/navigation.jsx @@ -15,14 +15,17 @@ import Navigation, { NavigationItem, NavigationGroup } from '../navigation'; const ThreatsNavigation = ( { selected, onSelect, sourceType = 'scan', statusFilter = 'all' } ) => { const { - plugins, - themes, - numThreats, - numCoreThreats, - numFilesThreats, - numDatabaseThreats, + results: { plugins, themes }, + counts: { + current: { + threats: numThreats, + core: numCoreThreats, + files: numFilesThreats, + database: numDatabaseThreats, + }, + }, hasRequiredPlan, - } = useProtectData( { sourceType, statusFilter } ); + } = useProtectData( { sourceType, filter: { status: statusFilter } } ); const { recordEvent } = useAnalyticsTracks(); const [ isSmallOrLarge ] = useBreakpointMatch( 'lg', '<' ); @@ -82,7 +85,7 @@ const ThreatsNavigation = ( { selected, onSelect, sourceType = 'scan', statusFil checked={ true } /> { firstDetected, fixedIn, fixedOn, - fixed_on, icon, fixable, id, @@ -210,7 +209,7 @@ const PaidList = ( { list } ) => { filename={ filename } firstDetected={ firstDetected } fixedIn={ fixedIn } - fixedOn={ fixedOn ?? fixed_on } + fixedOn={ fixedOn } icon={ icon } fixable={ fixable } id={ id } diff --git a/projects/plugins/protect/src/js/components/threats-list/use-threats-list.js b/projects/plugins/protect/src/js/components/threats-list/use-threats-list.js index 962268adca92f..df364a8af52a4 100644 --- a/projects/plugins/protect/src/js/components/threats-list/use-threats-list.js +++ b/projects/plugins/protect/src/js/components/threats-list/use-threats-list.js @@ -55,9 +55,11 @@ const flattenThreats = ( data, newData ) => { */ const useThreatsList = ( { source, status } = { source: 'scan', status: 'all' } ) => { const [ selected, setSelected ] = useState( 'all' ); - const { plugins, themes, core, files, database } = useProtectData( { + const { + results: { plugins, themes, core, files, database }, + } = useProtectData( { sourceType: source, - statusFilter: status, + filter: { status, key: selected }, } ); const { unsortedList, item } = useMemo( () => { @@ -66,19 +68,19 @@ const useThreatsList = ( { source, status } = { source: 'scan', status: 'all' } // Core, files, and database data threats are already grouped together, // so we just need to flatten them and add the appropriate icon. switch ( selected ) { - case 'wordpress': + case 'core': return { unsortedList: flattenThreats( core, { icon: coreIcon } ), item: core, }; case 'files': return { - unsortedList: flattenThreats( files, { icon: filesIcon } ), + unsortedList: flattenThreats( { threats: files }, { icon: filesIcon } ), item: files, }; case 'database': return { - unsortedList: flattenThreats( database, { icon: databaseIcon } ), + unsortedList: flattenThreats( { threats: database }, { icon: databaseIcon } ), item: database, }; default: @@ -109,8 +111,8 @@ const useThreatsList = ( { source, status } = { source: 'scan', status: 'all' } ...flattenThreats( core, { icon: coreIcon } ), ...flattenThreats( plugins, { icon: pluginsIcon } ), ...flattenThreats( themes, { icon: themesIcon } ), - ...flattenThreats( files, { icon: filesIcon } ), - ...flattenThreats( database, { icon: databaseIcon } ), + ...flattenThreats( { threats: files }, { icon: filesIcon } ), + ...flattenThreats( { threats: database }, { icon: databaseIcon } ), ], item: null, }; 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 index 5ddac7374ccd1..0beff65d51d4a 100644 --- a/projects/plugins/protect/src/js/hooks/use-protect-data/index.js +++ b/projects/plugins/protect/src/js/hooks/use-protect-data/index.js @@ -1,16 +1,25 @@ import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; import { useMemo } from 'react'; import { STORE_ID } from '../../state/store'; /** * 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 {string} options.statusFilter - 'all', 'fixed', or 'ignored'. + * @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. + * * @returns {object} The information available in Protect's initial state. */ -export default function useProtectData( { sourceType = 'scan', statusFilter = 'all' } = {} ) { +export default function useProtectData( + { sourceType, filter } = { + sourceType: 'scan', + filter: { status: null, key: null }, + } +) { const { status, scanHistory, jetpackScan, hasRequiredPlan } = useSelect( select => ( { status: select( STORE_ID ).getStatus(), scanHistory: select( STORE_ID ).getScanHistory(), @@ -18,90 +27,148 @@ export default function useProtectData( { sourceType = 'scan', statusFilter = 'a hasRequiredPlan: select( STORE_ID ).hasRequiredPlan(), } ) ); - const source = useMemo( () => { + 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 }; - // Filter the threats based on the status filter. - if ( statusFilter === 'all' ) { - return data; - } + // 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 => { + 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 = extension?.threats?.length + ? [ + ...extension.threats.filter( threat => { + // Status Filters + if ( filter.status && filter.status !== 'all' ) { + if ( threat.status !== filter.status ) { + return false; + } + } + // Key Filters + if ( filter.key && filter.key !== 'all' ) { + if ( filter.key === 'core' && key !== 'core' ) { + return false; + } + if ( filter.key === 'files' && key !== 'files' ) { + return false; + } + if ( filter.key === 'database' && key !== 'database' ) { + return false; + } + if ( + filter.key !== 'core' && + filter.key !== 'files' && + filter.key !== 'database' && + filter.key !== extension?.name + ) { + return false; + } + } + return true; + } ), + ] + : []; + + // Update the result object with the extension and its filtered threats. + result.results[ key ].push( { ...extension, threats: filteredThreats } ); + result.counts.current[ key ] += filteredThreats.length; + result.counts.current.threats += filteredThreats.length; + } ); + }; - return { - core: ( data.core || [] ) - .map( core => { - const threats = core.threats.filter( threat => threat.status === statusFilter ); - return { ...core, threats }; - } ) - .filter( core => core.threats.length > 0 ), - plugins: ( data.plugins || [] ).reduce( ( acc, plugin ) => { - const threats = plugin.threats.filter( threat => threat.status === statusFilter ); - if ( threats.length > 0 ) { - acc.push( { ...plugin, threats } ); + // Loop through the provided threats, and update the result object. + const processThreats = ( threatsToProcess, key ) => { + if ( ! Array.isArray( threatsToProcess ) ) { + return []; + } + threatsToProcess.forEach( threat => { + result.counts.all[ key ]++; + result.counts.all.threats++; + + // Status Filters + if ( filter.status && filter.status !== 'all' && threat?.status !== filter.status ) { + return; } - return acc; - }, [] ), - themes: ( data.themes || [] ).reduce( ( acc, theme ) => { - const threats = theme.threats.filter( threat => threat.status === statusFilter ); - if ( threats.length > 0 ) { - acc.push( { ...theme, threats } ); + + // Key Filters + if ( filter.key && filter.key !== 'all' && filter.key !== key ) { + return; } - return acc; - }, [] ), - files: ( data.files || [] ).filter( threat => threat.status === statusFilter ), - database: ( data.database || [] ).filter( threat => threat.status === statusFilter ), + + result.results[ key ].push( threat ); + result.counts.current[ key ]++; + result.counts.current.threats++; + } ); }; - }, [ sourceType, status, scanHistory, statusFilter ] ); - const numCoreThreats = useMemo( () => { - if ( 'history' === sourceType ) { - return ( source.core || [] ).reduce( - ( numThreats, core ) => numThreats + core.threats.length, - 0 - ); + // 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 ]; } - return source.core?.threats?.length || 0; - }, [ sourceType, source.core ] ); - const numPluginsThreats = useMemo( - () => - ( source.plugins || [] ).reduce( ( numThreats, plugin ) => { - return numThreats + plugin.threats.length; - }, 0 ), - [ source.plugins ] - ); + // Process the data + processExtensions( cores, 'core' ); + processExtensions( data?.plugins, 'plugins' ); + processExtensions( data?.themes, 'themes' ); + processThreats( data?.files, 'files' ); + processThreats( data?.database, 'database' ); - const numThemesThreats = useMemo( - () => - ( source.themes || [] ).reduce( ( numThreats, theme ) => { - return numThreats + theme.threats.length; - }, 0 ), - [ source.themes ] - ); - - const numFilesThreats = useMemo( () => source.files?.length || 0, [ source.files ] ); - - const numDatabaseThreats = useMemo( () => source.database?.length || 0, [ source.database ] ); + // Handle errors + if ( data.error ) { + result.error = { + message: data.errorMessage || __( 'An error occurred.', 'jetpack-protect' ), + code: data.errorCode || 500, + }; + } - const numThreats = - numCoreThreats + numPluginsThreats + numThemesThreats + numFilesThreats + numDatabaseThreats; + return result; + }, [ scanHistory, sourceType, status, filter ] ); return { - numThreats, - numCoreThreats, - numPluginsThreats, - numThemesThreats, - numFilesThreats, - numDatabaseThreats, - lastChecked: source.lastChecked || null, - error: source.error || false, - errorCode: source.errorCode || null, - errorMessage: source.errorMessage || null, - core: source.core || {}, - plugins: source.plugins || [], - themes: source.themes || [], - files: { threats: source.files || [] }, - database: { threats: source.database || [] }, - hasUncheckedItems: source.hasUncheckedItems, + results, + counts, + error, + lastChecked, + hasUncheckedItems, jetpackScan, hasRequiredPlan, }; diff --git a/projects/plugins/protect/src/js/index.tsx b/projects/plugins/protect/src/js/index.tsx index 5db2fab38eab3..8b5c507c3d995 100644 --- a/projects/plugins/protect/src/js/index.tsx +++ b/projects/plugins/protect/src/js/index.tsx @@ -3,6 +3,7 @@ import * as WPElement from '@wordpress/element'; import React, { useEffect } from 'react'; import { HashRouter, Routes, Route, useLocation, Navigate } from 'react-router-dom'; import Modal from './components/modal'; +import PaidPlanGate from './components/paid-plan-gate'; import { OnboardingRenderedContextProvider } from './hooks/use-onboarding'; import FirewallRoute from './routes/firewall'; import ScanRoute from './routes/scan'; @@ -42,8 +43,22 @@ function render() { } /> - } /> - } /> + + + + } + /> + + + + } + /> } /> } /> 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 73fcde3db9f15..639512a4a866e 100644 --- a/projects/plugins/protect/src/js/routes/scan/history/index.jsx +++ b/projects/plugins/protect/src/js/routes/scan/history/index.jsx @@ -20,14 +20,30 @@ const ScanHistoryRoute = () => { useAnalyticsTracks( { pageViewEventName: 'protect_scan_history' } ); const { filter = 'all' } = useParams(); - const { numThreats, error, errorMessage, errorCode, hasRequiredPlan } = useProtectData( { - sourceType: 'history', - } ); + const { item, list, selected, setSelected } = useThreatsList( { source: 'history', status: filter, } ); + const { counts, error, hasRequiredPlan } = useProtectData( { + sourceType: 'history', + filter: { status: filter }, + } ); + const { threats: numAllThreats } = counts.all; + + const { counts: fixedCounts } = useProtectData( { + sourceType: 'history', + filter: { status: 'fixed', key: selected }, + } ); + const { threats: numFixed } = fixedCounts.current; + + const { counts: ignoredCounts } = useProtectData( { + sourceType: 'history', + filter: { status: 'ignored', key: selected }, + } ); + const { threats: numIgnored } = ignoredCounts.current; + /** * Get the title for the threats list based on the selected filters and the amount of threats. */ @@ -68,7 +84,7 @@ const ScanHistoryRoute = () => { list.length ); } - case 'wordpress': + case 'core': switch ( filter ) { case 'fixed': return sprintf( @@ -216,6 +232,11 @@ const ScanHistoryRoute = () => { return ; } + // Remove the filter if there are no threats to show. + if ( list.length === 0 && filter !== 'all' ) { + return ; + } + return ( @@ -229,8 +250,8 @@ const ScanHistoryRoute = () => { : sprintf( /* translators: %s: Total number of threats */ __( '%1$s previously active %2$s', 'jetpack-protect' ), - numThreats, - numThreats === 1 ? 'threat' : 'threats' + numAllThreats, + numAllThreats === 1 ? 'threat' : 'threats' ) } /> @@ -242,8 +263,8 @@ const ScanHistoryRoute = () => { "An error occurred loading your site's threat history.", 'jetpack-protect' ) } - errorMessage={ errorMessage } - errorCode={ errorCode } + errorMessage={ error.message } + errorCode={ error.code } /> ) : ( @@ -263,7 +284,7 @@ const ScanHistoryRoute = () => {
{ getTitle() }
- +
diff --git a/projects/plugins/protect/src/js/routes/scan/history/status-filters.jsx b/projects/plugins/protect/src/js/routes/scan/history/status-filters.jsx index 4671ebb16e60e..e661980c3b96c 100644 --- a/projects/plugins/protect/src/js/routes/scan/history/status-filters.jsx +++ b/projects/plugins/protect/src/js/routes/scan/history/status-filters.jsx @@ -6,9 +6,13 @@ import ButtonGroup from '../../../components/button-group'; /** * Status Filters component. * + * @param {object} props - Component props. + * @param {number} props.numFixed - Number of fixed threats. + * @param {number} props.numIgnored - Number of ignored threats. + * * @returns {React.ReactNode} StatusFilters component. */ -export default function StatusFilters() { +export default function StatusFilters( { numFixed, numIgnored } ) { const navigate = useNavigate(); const { filter = 'all' } = useParams(); const navigateOnClick = useCallback( path => () => navigate( path ), [ navigate ] ); @@ -24,12 +28,14 @@ export default function StatusFilters() { { __( 'Fixed', 'jetpack-protect' ) } { __( 'Ignored', 'jetpack-protect' ) } diff --git a/projects/plugins/protect/src/js/state/selectors.js b/projects/plugins/protect/src/js/state/selectors.js index a438eb48cf69d..4878643e42cdb 100644 --- a/projects/plugins/protect/src/js/state/selectors.js +++ b/projects/plugins/protect/src/js/state/selectors.js @@ -8,7 +8,7 @@ import { SCAN_IN_PROGRESS_STATUSES, SCAN_STATUS_OPTIMISTICALLY_SCANNING } from ' * @returns {boolean} Whether a scan is in progress. */ const scanInProgress = state => { - const { status, error, lastChecked } = selectors.getStatus( state ); + const { status, lastChecked, error } = selectors.getStatus( state ); const unavailable = selectors.getScanIsUnavailable( state ); // When "optimistically" scanning, ignore any other status or error. @@ -16,13 +16,18 @@ const scanInProgress = state => { return true; } - // If there is an error or the scan is unavailable, scanning is not in progress. - if ( error || unavailable ) { + // If the scanner is unavailable, scanning is not in progress. + if ( unavailable ) { return false; } - // If the status is one of the scanning statuses, or if we have never checked, we are scanning. - if ( SCAN_IN_PROGRESS_STATUSES.includes( status.status ) || ! lastChecked ) { + // If the status is one of the scanning statuses, we are scanning. + if ( SCAN_IN_PROGRESS_STATUSES.includes( status ) ) { + return true; + } + + // If we have no record of a previous scan, we must be queueing up the initial scan. + if ( ! lastChecked && ! error ) { return true; } @@ -45,16 +50,6 @@ const scanError = state => { const unavailable = selectors.getScanIsUnavailable( state ); const isFetching = selectors.getStatusIsFetching( state ); - // When "optimistically" scanning, ignore any errors. - if ( SCAN_STATUS_OPTIMISTICALLY_SCANNING === status ) { - return null; - } - - // While still fetching the status, ignore any errors. - if ( isFetching ) { - return null; - } - // If the scan results include an error, return it. if ( error ) { return { code: errorCode, message: errorMessage }; @@ -68,6 +63,14 @@ const scanError = state => { }; } + // If there is no status and we are not requesting it, return an error. + if ( ! status && ! isFetching ) { + return { + code: 'scan_unavailable', + message: __( 'We are having problems scanning your site.', 'jetpack-protect' ), + }; + } + return null; }; diff --git a/projects/plugins/search/changelog/add-protect-threat-history b/projects/plugins/search/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/search/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/search/composer.lock b/projects/plugins/search/composer.lock index ff1dc8a9fa123..cceb68a7eefc6 100644 --- a/projects/plugins/search/composer.lock +++ b/projects/plugins/search/composer.lock @@ -1294,7 +1294,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1311,7 +1311,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/social/changelog/add-protect-threat-history b/projects/plugins/social/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/social/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/social/composer.lock b/projects/plugins/social/composer.lock index 1b691a28d6279..1b62ec25414be 100644 --- a/projects/plugins/social/composer.lock +++ b/projects/plugins/social/composer.lock @@ -1356,7 +1356,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1373,7 +1373,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/starter-plugin/changelog/add-protect-threat-history b/projects/plugins/starter-plugin/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/starter-plugin/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/starter-plugin/composer.lock b/projects/plugins/starter-plugin/composer.lock index d04e1f892cb6f..190eb10224715 100644 --- a/projects/plugins/starter-plugin/composer.lock +++ b/projects/plugins/starter-plugin/composer.lock @@ -1294,7 +1294,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1311,7 +1311,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}" diff --git a/projects/plugins/videopress/changelog/add-protect-threat-history b/projects/plugins/videopress/changelog/add-protect-threat-history new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/videopress/changelog/add-protect-threat-history @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/videopress/composer.lock b/projects/plugins/videopress/composer.lock index f4954022276ba..eb96e85249590 100644 --- a/projects/plugins/videopress/composer.lock +++ b/projects/plugins/videopress/composer.lock @@ -1294,7 +1294,7 @@ "dist": { "type": "path", "url": "../../packages/protect-models", - "reference": "a79c18207b3476214e4be25eb5184c452c952ea9" + "reference": "7acb119f496f32361379b236b6dbbcca68680d4d" }, "require": { "php": ">=7.0" @@ -1311,7 +1311,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.1.x-dev" + "dev-trunk": "0.2.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-protect-models/compare/v${old}...v${new}"