+ { 'fixed' === status
+ ? __( 'Fixed', 'jetpack-protect' )
+ : __( 'Ignored', 'jetpack-protect', /* dummy arg to avoid bad minification */ 0 ) }
+
+);
+
export const PaidAccordionItem = ( {
id,
title,
@@ -23,12 +61,12 @@ export const PaidAccordionItem = ( {
firstDetected,
fixedOn,
onOpen,
+ status,
} ) => {
const accordionData = useContext( PaidAccordionContext );
const open = accordionData?.open === id;
const setOpen = accordionData?.setOpen;
const threatsAreFixing = useSelect( select => select( STORE_ID ).getThreatsAreFixing() );
- const { viewingScanHistory } = useScanHistory();
const bodyClassNames = clsx( styles[ 'accordion-body' ], {
[ styles[ 'accordion-body-open' ] ]: open,
@@ -46,49 +84,6 @@ export const PaidAccordionItem = ( {
const [ isSmall ] = useBreakpointMatch( [ 'sm', 'lg' ], [ null, '<' ] );
- const FixDetails = ( { date, isFixed } ) => (
-
- { 'fixed' === status
- ? __( 'Fixed', 'jetpack-protect' )
- : __( 'Ignored', 'jetpack-protect', /* dummy arg to avoid bad minification */ 0 ) }
-
- );
-
return (
diff --git a/projects/plugins/protect/src/js/components/protect-check-icon/index.tsx b/projects/plugins/protect/src/js/components/protect-check-icon/index.tsx
new file mode 100644
index 0000000000000..3727aa3eae3a0
--- /dev/null
+++ b/projects/plugins/protect/src/js/components/protect-check-icon/index.tsx
@@ -0,0 +1,25 @@
+import { type JSX } from 'react';
+
+/**
+ * Protect Shield and Checkmark SVG Icon
+ *
+ * @returns {JSX.Element} Protect Shield and Checkmark SVG Icon
+ */
+export default function ProtectCheck(): JSX.Element {
+ return (
+
+
+
+
+ );
+}
diff --git a/projects/plugins/protect/src/js/components/scan-button/index.jsx b/projects/plugins/protect/src/js/components/scan-button/index.jsx
new file mode 100644
index 0000000000000..95b1541af8f46
--- /dev/null
+++ b/projects/plugins/protect/src/js/components/scan-button/index.jsx
@@ -0,0 +1,34 @@
+import { Button } from '@automattic/jetpack-components';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { __ } from '@wordpress/i18n';
+import React from 'react';
+import { STORE_ID } from '../../state/store';
+
+/**
+ * Scan Button Component
+ *
+ * @param {object} props - The component props.
+ * @returns {React.ReactElement} Button that triggers a scan on click.
+ */
+export default function ScanButton( { ...props } ) {
+ const { scan } = useDispatch( STORE_ID );
+ const scanIsEnqueuing = useSelect( select => select( STORE_ID ).getScanIsEnqueuing(), [] );
+
+ const handleScanClick = () => {
+ return event => {
+ event.preventDefault();
+ scan();
+ };
+ };
+
+ return (
+
+ { __( 'Scan now', 'jetpack-protect' ) }
+
+ );
+}
diff --git a/projects/plugins/protect/src/js/components/summary/index.jsx b/projects/plugins/protect/src/js/components/summary/index.jsx
index fd416021cc138..d9a0d3623e57a 100644
--- a/projects/plugins/protect/src/js/components/summary/index.jsx
+++ b/projects/plugins/protect/src/js/components/summary/index.jsx
@@ -1,171 +1,49 @@
-import {
- Container,
- Col,
- Text,
- Title,
- getIconBySlug,
- Button,
- useBreakpointMatch,
-} from '@automattic/jetpack-components';
-import { useDispatch, useSelect } from '@wordpress/data';
+import { useBreakpointMatch } from '@automattic/jetpack-components';
import { dateI18n } from '@wordpress/date';
import { __, sprintf } from '@wordpress/i18n';
import React, { useState } from 'react';
import useProtectData from '../../hooks/use-protect-data';
-import useScanHistory from '../../hooks/use-scan-history';
-import { STORE_ID } from '../../state/store';
+import ScanSectionHeader from '../../routes/scan/scan-section-header';
import OnboardingPopover from '../onboarding-popover';
-import styles from './styles.module.scss';
const Summary = () => {
const [ isSm ] = useBreakpointMatch( 'sm' );
- const {
- filter,
- viewingScanHistory,
- allScanHistoryIsLoading,
- ignoredScanHistoryIsLoading,
- fixedScanHistoryIsLoading,
- toggleAllScanHistory,
- toggleIgnoredScanHistory,
- toggleFixedScanHistory,
- handleHistoryClick,
- handleCurrentClick,
- } = useScanHistory();
const { numThreats, lastChecked, hasRequiredPlan } = useProtectData();
- const scanIsEnqueuing = useSelect( select => select( STORE_ID ).getScanIsEnqueuing() );
- const { scan } = useDispatch( STORE_ID );
- const Icon = getIconBySlug( 'protect' );
// Popover anchors
const [ dailyScansPopoverAnchor, setDailyScansPopoverAnchor ] = useState( null );
- const [ dailyAndManualScansPopoverAnchor, setDailyAndManualScansPopoverAnchor ] =
- useState( null );
-
- const handleScanClick = () => {
- return event => {
- event.preventDefault();
- scan();
- };
- };
-
- const renderScanOptions = () => (
- <>
-
- { __( 'Scan now', 'jetpack-protect' ) }
-
-
-
- { __( 'History', 'jetpack-protect' ) }
-
- >
- );
-
- const renderHistoryButtons = () => (
- <>
-
- { __( 'Current', 'jetpack-protect' ) }
-
-
- { __( 'All', 'jetpack-protect' ) }
-
-
- { __( 'Ignored', 'jetpack-protect' ) }
-
-
- { __( 'Fixed', 'jetpack-protect' ) }
-
- >
- );
return (
-
-
-
-
-
-
- { ! viewingScanHistory ? (
-
- { sprintf(
- /* translators: %s: Latest check date */
- __( 'Latest results as of %s', 'jetpack-protect' ),
- dateI18n( 'F jS', lastChecked )
- ) }
-
- ) : (
-
- { sprintf(
- /* translators: %s: Filter applied */
- __( 'Scan history of %s threats', 'jetpack-protect' ),
- filter
- ) }
-
- ) }
- { ! hasRequiredPlan && (
-
- ) }
-
- { numThreats > 0 && (
-
- { sprintf(
- /* translators: %s: Total number of threats */
- __( '%1$s %2$s found', 'jetpack-protect' ),
- numThreats,
- numThreats === 1 ? 'threat' : 'threats'
- ) }
-
+
0
+ ? sprintf(
+ /* translators: %s: Total number of threats */
+ __( '%1$s %2$s found', 'jetpack-protect' ),
+ numThreats,
+ numThreats === 1 ? 'threat' : 'threats'
+ )
+ : undefined
+ }
+ subtitle={
+ <>
+
+ { sprintf(
+ /* translators: %s: Latest check date */
+ __( 'Latest results as of %s', 'jetpack-protect' ),
+ dateI18n( 'F jS', lastChecked )
) }
- { hasRequiredPlan && (
- <>
- { ! viewingScanHistory && numThreats === 0 && renderScanOptions() }
- { viewingScanHistory && renderHistoryButtons() }
- >
+ { ! hasRequiredPlan && (
+
) }
-
-
-
+ >
+ }
+ />
);
};
diff --git a/projects/plugins/protect/src/js/components/summary/styles.module.scss b/projects/plugins/protect/src/js/components/summary/styles.module.scss
deleted file mode 100644
index e72a61b7c0bfd..0000000000000
--- a/projects/plugins/protect/src/js/components/summary/styles.module.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-.summary {
- > :first-child {
- flex: 1;
- }
-
- @media ( min-width: 960px ) {
- display: flex;
- align-items: flex-start;
- }
-}
-
-.summary__title {
- display: flex;
- align-items: center;
- color: var( --jp-black );
-}
-
-.summary__icon {
- margin-left: -4px; // this fix a blank space on right/left from @wordpress/icons
- margin-right: var( --spacing-base ); // 8px
-}
-
-.summary__scan-button, .summary__history-button {
- margin-top: calc( var( --spacing-base ) * 2 ); // 16px
- width: 100%;
-
- @media ( min-width: 960px ) {
- margin-top: 0;
- margin-left: calc( var( --spacing-base ) * 2 ); // 16px
- width: auto;
- }
-}
\ No newline at end of file
diff --git a/projects/plugins/protect/src/js/components/threats-list/empty.jsx b/projects/plugins/protect/src/js/components/threats-list/empty.jsx
index 18c6d801953d8..62e9c5c715f96 100644
--- a/projects/plugins/protect/src/js/components/threats-list/empty.jsx
+++ b/projects/plugins/protect/src/js/components/threats-list/empty.jsx
@@ -3,7 +3,7 @@ import { createInterpolateElement } from '@wordpress/element';
import { sprintf, __, _n } from '@wordpress/i18n';
import { useMemo } from 'react';
import useProtectData from '../../hooks/use-protect-data';
-import useScanHistory from '../../hooks/use-scan-history';
+import ScanButton from '../scan-button';
import styles from './styles.module.scss';
const ProtectCheck = () => (
@@ -84,8 +84,7 @@ const timeSince = date => {
};
const EmptyList = () => {
- const { lastChecked } = useProtectData();
- const { viewingScanHistory } = useScanHistory();
+ const { lastChecked, hasRequiredPlan } = useProtectData();
const timeSinceLastScan = useMemo( () => {
return lastChecked ? timeSince( Date.parse( lastChecked ) ) : null;
@@ -97,26 +96,22 @@ const EmptyList = () => {
{ __( "Don't worry about a thing", 'jetpack-protect' ) }
-
- { viewingScanHistory
- ? __(
- 'So far, there are no threats in your scan history for the current filter.',
+
+ { createInterpolateElement(
+ sprintf(
+ // translators: placeholder is the amount of time since the last scan, i.e. "5 minutes ago".
+ __(
+ 'The last Protect scan ran %s and everything looked great.',
'jetpack-protect'
- )
- : createInterpolateElement(
- sprintf(
- // translators: placeholder is the amount of time since the last scan, i.e. "5 minutes ago".
- __(
- 'The last Protect scan ran %s and everything looked great.',
- 'jetpack-protect'
- ),
- timeSinceLastScan
- ),
- {
- strong: ,
- }
- ) }
+ ),
+ timeSinceLastScan
+ ),
+ {
+ strong: ,
+ }
+ ) }
+ { hasRequiredPlan && }
);
};
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 92f21af34c928..0fbd9cea0809f 100644
--- a/projects/plugins/protect/src/js/components/threats-list/index.jsx
+++ b/projects/plugins/protect/src/js/components/threats-list/index.jsx
@@ -1,11 +1,11 @@
import { Container, Col, Title, Button, useBreakpointMatch } from '@automattic/jetpack-components';
-import { useDispatch, useSelect } from '@wordpress/data';
+import { useDispatch } from '@wordpress/data';
import { __, sprintf } from '@wordpress/i18n';
import React, { useCallback, useState } from 'react';
import useProtectData from '../../hooks/use-protect-data';
-import useScanHistory from '../../hooks/use-scan-history';
import { STORE_ID } from '../../state/store';
import OnboardingPopover from '../onboarding-popover';
+import ScanButton from '../scan-button';
import EmptyList from './empty';
import FreeList from './free-list';
import ThreatsNavigation from './navigation';
@@ -15,19 +15,17 @@ import useThreatsList from './use-threats-list';
const ThreatsList = () => {
const { hasRequiredPlan } = useProtectData();
- const { viewingScanHistory, handleHistoryClick, allScanHistoryIsLoading } = useScanHistory();
const { item, list, selected, setSelected } = useThreatsList();
const fixableList = list.filter( obj => obj.fixable );
const [ isSm ] = useBreakpointMatch( 'sm' );
- const { setModal } = useDispatch( STORE_ID );
- const { scan } = useDispatch( STORE_ID );
- const scanIsEnqueuing = useSelect( select => select( STORE_ID ).getScanIsEnqueuing() );
-
// Popover anchors
const [ yourScanResultsPopoverAnchor, setYourScanResultsPopoverAnchor ] = useState( null );
- const [ fixAllThreatsPopoverAnchor, setFixAllThreatsPopoverAnchor ] = useState( null );
const [ understandSeverityPopoverAnchor, setUnderstandSeverityPopoverAnchor ] = useState( null );
+
+ const { setModal } = useDispatch( STORE_ID );
+
+ const [ fixAllThreatsPopoverAnchor, setFixAllThreatsPopoverAnchor ] = useState( null );
const [ dailyAndManualScansPopoverAnchor, setDailyAndManualScansPopoverAnchor ] =
useState( null );
@@ -41,13 +39,6 @@ const ThreatsList = () => {
};
};
- const handleScanClick = () => {
- return event => {
- event.preventDefault();
- scan();
- };
- };
-
const getTitle = useCallback( () => {
switch ( selected ) {
case 'all':
@@ -109,14 +100,13 @@ const ThreatsList = () => {
<>
{ getTitle() }
- { hasRequiredPlan && ! viewingScanHistory && (
- <>
+ { hasRequiredPlan && (
+
{ fixableList.length > 0 && (
<>
{ sprintf(
@@ -132,29 +122,13 @@ const ThreatsList = () => {
/>
>
) }
-
- { __( 'Scan now', 'jetpack-protect' ) }
-
+
-
- { __( 'History', 'jetpack-protect' ) }
-
- >
+
) }
{ hasRequiredPlan ? (
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 d460a8f86e870..c6197c56df13e 100644
--- a/projects/plugins/protect/src/js/components/threats-list/navigation.jsx
+++ b/projects/plugins/protect/src/js/components/threats-list/navigation.jsx
@@ -8,12 +8,12 @@ import {
code as filesIcon,
grid as databaseIcon,
} from '@wordpress/icons';
-import { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import useProtectData from '../../hooks/use-protect-data';
import Navigation, { NavigationItem, NavigationGroup } from '../navigation';
-const ThreatsNavigation = ( { selected, onSelect } ) => {
+const ThreatsNavigation = ( { selected, onSelect, sourceType = 'scan', statusFilter = 'all' } ) => {
const {
plugins,
themes,
@@ -22,7 +22,7 @@ const ThreatsNavigation = ( { selected, onSelect } ) => {
numFilesThreats,
numDatabaseThreats,
hasRequiredPlan,
- } = useProtectData();
+ } = useProtectData( { sourceType, statusFilter } );
const { recordEvent } = useAnalyticsTracks();
const [ isSmallOrLarge ] = useBreakpointMatch( 'lg', '<' );
@@ -51,6 +51,16 @@ const ThreatsNavigation = ( { selected, onSelect } ) => {
recordEvent( 'jetpack_protect_navigation_database_click' );
}, [ recordEvent ] );
+ const allLabel = useMemo( () => {
+ if ( statusFilter === 'fixed' ) {
+ return __( 'All fixed threats', 'jetpack-protect' );
+ }
+ if ( statusFilter === 'ignored' ) {
+ return __( 'All ignored threats', 'jetpack-protect' );
+ }
+ return __( 'All threats', 'jetpack-protect' );
+ }, [ statusFilter ] );
+
return (
{
{
const threatsAreFixing = useSelect( select => select( STORE_ID ).getThreatsAreFixing() );
- const { viewingScanHistory } = useScanHistory();
const { setModal } = useDispatch( STORE_ID );
const { recordEvent } = useAnalyticsTracks();
@@ -72,6 +71,7 @@ const ThreatAccordionItem = ( {
severity={ severity }
firstDetected={ firstDetected }
fixedOn={ fixedOn }
+ status={ status }
onOpen={ useCallback( () => {
if ( ! [ 'core', 'plugin', 'theme', 'file', 'database' ].includes( type ) ) {
return;
@@ -82,7 +82,9 @@ const ThreatAccordionItem = ( {
{ description && (
- { __( 'What is the problem?', 'jetpack-protect' ) }
+ { status !== 'fixed'
+ ? __( 'What is the problem?', 'jetpack-protect' )
+ : __( 'What was the problem?', 'jetpack-protect' ) }
{ description }
{ learnMoreButton }
@@ -106,7 +108,7 @@ const ThreatAccordionItem = ( {
) }
{ context &&
}
{ diff &&
}
- { fixedIn && (
+ { fixedIn && status !== 'fixed' && (
{ __( 'How to fix it?', 'jetpack-protect' ) }
@@ -121,12 +123,12 @@ const ThreatAccordionItem = ( {
) }
{ ! description && { learnMoreButton }
}
- { ! viewingScanHistory && (
+ { status !== 'ignored' && status !== 'fixed' && (
{ __( 'Ignore threat', 'jetpack-protect' ) }
) }
- { fixable && (
+ { fixable && status !== 'fixed' && (
{ __( 'Fix threat', 'jetpack-protect' ) }
@@ -195,9 +197,7 @@ const PaidList = ( { list } ) => {
diff,
filename,
firstDetected, // todo: still needs a proper fix
- first_detected,
fixedIn,
- fixed_in,
fixedOn,
fixed_on,
icon,
@@ -211,14 +211,15 @@ const PaidList = ( { list } ) => {
title,
type,
version,
+ status,
} ) => (
{
title={ title }
type={ type }
version={ version }
+ status={ status }
/>
)
) }
diff --git a/projects/plugins/protect/src/js/components/threats-list/styles.module.scss b/projects/plugins/protect/src/js/components/threats-list/styles.module.scss
index f7adf047d2301..256c5838f61ac 100644
--- a/projects/plugins/protect/src/js/components/threats-list/styles.module.scss
+++ b/projects/plugins/protect/src/js/components/threats-list/styles.module.scss
@@ -40,8 +40,9 @@
margin-bottom: 0;
}
-.list-header-button {
- margin-left: calc( var( --spacing-base ) * 2 ); // 16px
+.list-header__controls {
+ display: flex;
+ gap: calc( var( --spacing-base ) * 2 ); // 16px
}
.threat-footer {
@@ -104,14 +105,6 @@
display: none;
}
- .list-header-button {
- flex: 1;
-
- &:first-of-type {
- margin-left: 0;
- }
- }
-
.threat-footer {
justify-content: center;
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 c6d0ceb568c36..add4f5b073f73 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
@@ -5,7 +5,7 @@ import {
code as filesIcon,
grid as databaseIcon,
} from '@wordpress/icons';
-import { useMemo, useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import useProtectData from '../../hooks/use-protect-data';
const sortThreats = ( a, b ) => b.severity - a.severity;
@@ -41,6 +41,10 @@ const flattenThreats = ( data, newData ) => {
/**
* Threats List Hook
*
+ * @param {object} args - Arguments for the hook.
+ * @param {string} args.source - "scan" or "history".
+ * @param {string} args.status - "all", "fixed", or "ignored".
+ * ---
* @typedef {object} UseThreatsList
* @property {object} item - The selected threat category.
* @property {object[]} list - The list of threats to display.
@@ -49,9 +53,12 @@ const flattenThreats = ( data, newData ) => {
* ---
* @returns {UseThreatsList} useThreatsList hook.
*/
-const useThreatsList = () => {
+const useThreatsList = ( { source, status } = { source: 'scan', status: 'all' } ) => {
const [ selected, setSelected ] = useState( 'all' );
- const { plugins, themes, core, files, database } = useProtectData();
+ const { plugins, themes, core, files, database } = useProtectData( {
+ sourceType: source,
+ statusFilter: status,
+ } );
const { unsortedList, item } = useMemo( () => {
// If a specific threat category is selected, filter for and flatten the category's threats.
@@ -113,6 +120,12 @@ const useThreatsList = () => {
return [ ...unsortedList ].sort( sortThreats );
}, [ unsortedList ] );
+ useEffect( () => {
+ if ( selected !== 'all' && status !== 'all' && item === null ) {
+ setSelected( 'all' );
+ }
+ }, [ selected, status, item ] );
+
return {
item,
list,
diff --git a/projects/plugins/protect/src/js/global.d.ts b/projects/plugins/protect/src/js/global.d.ts
new file mode 100644
index 0000000000000..d5cf927a7cd3e
--- /dev/null
+++ b/projects/plugins/protect/src/js/global.d.ts
@@ -0,0 +1 @@
+declare module '*.scss';
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 8e7cddaa68512..c623f0201134a 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,33 +1,61 @@
import { useSelect } from '@wordpress/data';
import { useMemo } from 'react';
import { STORE_ID } from '../../state/store';
-import useScanHistory from '../use-scan-history';
/**
* 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'.
* @returns {object} The information available in Protect's initial state.
*/
-export default function useProtectData() {
- const { viewingScanHistory, scanHistory } = useScanHistory();
-
- const { status, jetpackScan, hasRequiredPlan } = useSelect( select => ( {
+export default function useProtectData( { sourceType = 'scan', statusFilter = 'all' } = {} ) {
+ const { status, scanHistory, jetpackScan, hasRequiredPlan } = useSelect( select => ( {
status: select( STORE_ID ).getStatus(),
+ scanHistory: select( STORE_ID ).getScanHistory(),
jetpackScan: select( STORE_ID ).getJetpackScan(),
hasRequiredPlan: select( STORE_ID ).hasRequiredPlan(),
} ) );
- const source = viewingScanHistory ? scanHistory : status;
+ const source = useMemo( () => {
+ const data = sourceType === 'history' ? { ...scanHistory } : { ...status };
+
+ // Filter the threats based on the status filter.
+ if ( statusFilter === 'all' ) {
+ return data;
+ }
+
+ return {
+ core: ( data.core || [] ).filter( threat => threat.status === statusFilter ),
+ plugins: ( data.plugins || [] ).reduce( ( acc, plugin ) => {
+ const threats = plugin.threats.filter( threat => threat.status === statusFilter );
+ if ( threats.length > 0 ) {
+ acc.push( { ...plugin, threats } );
+ }
+ 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 } );
+ }
+ return acc;
+ }, [] ),
+ files: ( data.files || [] ).filter( threat => threat.status === statusFilter ),
+ database: ( data.database || [] ).filter( threat => threat.status === statusFilter ),
+ };
+ }, [ sourceType, status, scanHistory, statusFilter ] );
const numCoreThreats = useMemo( () => {
- if ( viewingScanHistory ) {
+ if ( 'history' === sourceType ) {
return ( source.core || [] ).reduce(
( numThreats, core ) => numThreats + core.threats.length,
0
);
}
return source.core?.threats?.length || 0;
- }, [ viewingScanHistory, source.core ] );
+ }, [ sourceType, source.core ] );
const numPluginsThreats = useMemo(
() =>
diff --git a/projects/plugins/protect/src/js/hooks/use-scan-history/index.js b/projects/plugins/protect/src/js/hooks/use-scan-history/index.js
deleted file mode 100644
index cdee0ea2380ec..0000000000000
--- a/projects/plugins/protect/src/js/hooks/use-scan-history/index.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { useDispatch, useSelect } from '@wordpress/data';
-import { useCallback, useState } from 'react';
-import API from '../../api';
-import { STORE_ID } from '../../state/store';
-
-/**
- * Get parsed data from the initial state
- *
- * @returns {object} The information available in Protect's initial state.
- */
-export default function useScanHistory() {
- const { viewingScanHistory, scanHistory } = useSelect( select => ( {
- viewingScanHistory: select( STORE_ID ).getViewingScanHistory(),
- scanHistory: select( STORE_ID ).getScanHistory(),
- } ) );
-
- const { setViewingScanHistory, setScanHistory } = useDispatch( STORE_ID );
-
- const [ filter, setFilter ] = useState( 'all' );
- const [ allScanHistoryIsLoading, setAllScanHistoryIsLoading ] = useState( false );
- const [ ignoredScanHistoryIsLoading, setIgnoredScanHistoryIsLoading ] = useState( false );
- const [ fixedScanHistoryIsLoading, setFixedScanHistoryIsLoading ] = useState( false );
-
- const toggleAllScanHistory = useCallback( () => {
- setAllScanHistoryIsLoading( true );
- return API.fetchScanHistory( [ 'ignored', 'fixed' ] ).then( filteredScanHistory => {
- setScanHistory( filteredScanHistory );
- setFilter( 'all' );
- setAllScanHistoryIsLoading( false );
- } );
- }, [ setScanHistory, setAllScanHistoryIsLoading ] );
-
- const toggleIgnoredScanHistory = useCallback( () => {
- setIgnoredScanHistoryIsLoading( true );
- return API.fetchScanHistory( [ 'ignored' ] ).then( filteredScanHistory => {
- setScanHistory( filteredScanHistory );
- setFilter( 'ignored' );
- setIgnoredScanHistoryIsLoading( false );
- } );
- }, [ setScanHistory ] );
-
- const toggleFixedScanHistory = useCallback( () => {
- setFixedScanHistoryIsLoading( true );
- return API.fetchScanHistory( [ 'fixed' ] ).then( filteredScanHistory => {
- setScanHistory( filteredScanHistory );
- setFilter( 'fixed' );
- setFixedScanHistoryIsLoading( false );
- } );
- }, [ setScanHistory ] );
-
- const handleHistoryClick = useCallback( () => {
- toggleAllScanHistory().then( () => {
- setViewingScanHistory( true );
- } );
- }, [ toggleAllScanHistory, setViewingScanHistory ] );
-
- const handleCurrentClick = useCallback( () => {
- setViewingScanHistory( false );
- }, [ setViewingScanHistory ] );
-
- return {
- filter,
- viewingScanHistory,
- scanHistory,
- allScanHistoryIsLoading,
- ignoredScanHistoryIsLoading,
- fixedScanHistoryIsLoading,
- toggleIgnoredScanHistory,
- toggleFixedScanHistory,
- toggleAllScanHistory,
- handleHistoryClick,
- handleCurrentClick,
- };
-}
diff --git a/projects/plugins/protect/src/js/index.tsx b/projects/plugins/protect/src/js/index.tsx
index ad2bbcc3db090..5db2fab38eab3 100644
--- a/projects/plugins/protect/src/js/index.tsx
+++ b/projects/plugins/protect/src/js/index.tsx
@@ -1,11 +1,12 @@
import { ThemeProvider } from '@automattic/jetpack-components';
import * as WPElement from '@wordpress/element';
import React, { useEffect } from 'react';
-import { HashRouter, Routes, Route, useLocation } from 'react-router-dom';
+import { HashRouter, Routes, Route, useLocation, Navigate } from 'react-router-dom';
import Modal from './components/modal';
import { OnboardingRenderedContextProvider } from './hooks/use-onboarding';
import FirewallRoute from './routes/firewall';
import ScanRoute from './routes/scan';
+import ScanHistoryRoute from './routes/scan/history';
import { initStore } from './state/store';
import './styles.module.scss';
@@ -40,8 +41,11 @@ 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
new file mode 100644
index 0000000000000..1880402401940
--- /dev/null
+++ b/projects/plugins/protect/src/js/routes/scan/history/index.jsx
@@ -0,0 +1,267 @@
+import { AdminSectionHero, Container, Col, H3, Text, Title } from '@automattic/jetpack-components';
+import { __, _n, sprintf } from '@wordpress/i18n';
+import { useCallback } from 'react';
+import { useParams } from 'react-router-dom';
+import AdminPage from '../../../components/admin-page';
+import ProtectCheck from '../../../components/protect-check-icon';
+import ScanFooter from '../../../components/scan-footer';
+import ThreatsNavigation from '../../../components/threats-list/navigation';
+import PaidList from '../../../components/threats-list/paid-list';
+import useThreatsList from '../../../components/threats-list/use-threats-list';
+import useAnalyticsTracks from '../../../hooks/use-analytics-tracks';
+import useProtectData from '../../../hooks/use-protect-data';
+import ScanSectionHeader from '../scan-section-header';
+import StatusFilters from './status-filters';
+import styles from './styles.module.scss';
+
+const ScanHistoryRoute = () => {
+ // Track page view.
+ useAnalyticsTracks( { pageViewEventName: 'protect_scan_history' } );
+
+ const { filter = 'all' } = useParams();
+ const { numThreats } = useProtectData( { sourceType: 'history' } );
+ const { item, list, selected, setSelected } = useThreatsList( {
+ source: 'history',
+ status: filter,
+ } );
+
+ /**
+ * 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' );
+ 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 'wordpress':
+ 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 ] );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ { list.length > 0 ? (
+
+ ) : (
+
+
+
+ { __( "Don't worry about a thing", 'jetpack-protect' ) }
+
+
+ { __(
+ 'There are no threats in your scan history for the selected filters.',
+ 'jetpack-protect'
+ ) }
+
+
+ ) }
+
+
+
+
+
+
+
+ );
+};
+
+export default ScanHistoryRoute;
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
new file mode 100644
index 0000000000000..4671ebb16e60e
--- /dev/null
+++ b/projects/plugins/protect/src/js/routes/scan/history/status-filters.jsx
@@ -0,0 +1,38 @@
+import { __ } from '@wordpress/i18n';
+import React, { useCallback } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import ButtonGroup from '../../../components/button-group';
+
+/**
+ * Status Filters component.
+ *
+ * @returns {React.ReactNode} StatusFilters component.
+ */
+export default function StatusFilters() {
+ const navigate = useNavigate();
+ const { filter = 'all' } = useParams();
+ const navigateOnClick = useCallback( path => () => navigate( path ), [ navigate ] );
+
+ return (
+
+
+ { __( 'All', 'jetpack-protect' ) }
+
+
+ { __( 'Fixed', 'jetpack-protect' ) }
+
+
+ { __( 'Ignored', 'jetpack-protect' ) }
+
+
+ );
+}
diff --git a/projects/plugins/protect/src/js/routes/scan/history/styles.module.scss b/projects/plugins/protect/src/js/routes/scan/history/styles.module.scss
new file mode 100644
index 0000000000000..fb60ceedc7148
--- /dev/null
+++ b/projects/plugins/protect/src/js/routes/scan/history/styles.module.scss
@@ -0,0 +1,65 @@
+.summary {
+ > :first-child {
+ flex: 1;
+ }
+
+ @media ( min-width: 960px ) {
+ display: flex;
+ align-items: flex-start;
+ }
+}
+
+.summary__title {
+ display: flex;
+ align-items: center;
+ color: var( --jp-black );
+}
+
+.summary__icon {
+ margin-left: -4px; // this fix a blank space on right/left from @wordpress/icons
+ margin-right: var( --spacing-base ); // 8px
+}
+
+.summary__actions {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: calc( var( --spacing-base ) * 2 ); // 16px
+}
+
+.empty {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+ max-height: 600px;
+ flex-direction: column;
+}
+
+.list-header {
+ display: flex;
+ align-items: flex-end;
+ margin-bottom: calc( var( --spacing-base ) * 2.25 ); // 18px
+}
+
+.list-title {
+ flex: 1;
+ margin-bottom: 0;
+}
+
+.list-header__controls {
+ display: flex;
+ gap: calc( var( --spacing-base ) * 2 ); // 16px
+}
+
+@media ( max-width: 599px ) {
+
+ .list-header {
+ margin-bottom: calc( var( --spacing-base ) * 3 ); // 24px
+ }
+
+ .list-title {
+ display: none;
+ }
+}
diff --git a/projects/plugins/protect/src/js/routes/scan/index.jsx b/projects/plugins/protect/src/js/routes/scan/index.jsx
index 39547a7cc982e..75d5e25bfa369 100644
--- a/projects/plugins/protect/src/js/routes/scan/index.jsx
+++ b/projects/plugins/protect/src/js/routes/scan/index.jsx
@@ -1,8 +1,8 @@
-import { AdminSectionHero, Container, Col, H3, Text, Button } from '@automattic/jetpack-components';
+import { AdminSectionHero, Container, Col, H3, Text } from '@automattic/jetpack-components';
import { useConnectionErrorNotice, ConnectionError } from '@automattic/jetpack-connection';
import { Spinner } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
-import { sprintf, __ } from '@wordpress/i18n';
+import { __ } from '@wordpress/i18n';
import React, { useEffect, useMemo } from 'react';
import AdminPage from '../../components/admin-page';
import AlertSVGIcon from '../../components/alert-icon';
@@ -14,7 +14,6 @@ import ThreatsList from '../../components/threats-list';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import { OnboardingContext } from '../../hooks/use-onboarding';
import useProtectData from '../../hooks/use-protect-data';
-import useScanHistory from '../../hooks/use-scan-history';
import { STORE_ID } from '../../state/store';
import inProgressImage from './in-progress.png';
import onboardingSteps from './onboarding-steps';
@@ -39,66 +38,23 @@ const ConnectionErrorCol = () => {
);
};
-const ButtonCol = ( {
- viewingScanHistory,
- handleHistoryClick,
- handleCurrentClick,
- allScanHistoryIsLoading,
-} ) => {
- return (
-
- { ! viewingScanHistory ? (
-
- { __( 'History', 'jetpack-protect' ) }
-
- ) : (
-
- { __( 'Current', 'jetpack-protect' ) }
-
- ) }
-
- );
-};
-
-const HeaderContainer = ( { displayButtonCol } ) => {
- const { viewingScanHistory, handleCurrentClick, handleHistoryClick, allScanHistoryIsLoading } =
- useScanHistory();
-
+const HeaderContainer = () => {
return (
- { displayButtonCol && (
-
- ) }
);
};
-const ErrorSection = ( { viewingScanHistory, errorMessage, errorCode } ) => {
- const activityContext = viewingScanHistory
- ? 'retrieving your scan history'
- : 'scanning your site';
- const baseErrorMessage = sprintf(
- /* translators: %s is the activity context, like "scanning your site" or "retrieving your scan history" */
- __( 'We are having problems %s.', 'jetpack-protect' ),
- activityContext
- );
+const ErrorSection = ( { errorMessage, errorCode } ) => {
+ const baseErrorMessage = __( 'We are having problems scanning your site.', 'jetpack-protect' );
let displayErrorMessage = errorMessage ? `${ errorMessage } (${ errorCode }).` : baseErrorMessage;
displayErrorMessage += ' ' + __( 'Try again in a few minutes.', 'jetpack-protect' );
return (
<>
-
+
@@ -121,7 +77,7 @@ const ErrorSection = ( { viewingScanHistory, errorMessage, errorCode } ) => {
const ScanningSection = ( { currentProgress } ) => {
return (
<>
-
+
@@ -167,8 +123,8 @@ const ScanningSection = ( { currentProgress } ) => {
const DefaultSection = () => {
return (
<>
-
-
+
+
@@ -181,7 +137,6 @@ const DefaultSection = () => {
};
const ScanPage = () => {
- const { viewingScanHistory } = useScanHistory();
const { lastChecked, error, errorCode, errorMessage, hasRequiredPlan } = useProtectData();
const { refreshStatus } = useDispatch( STORE_ID );
const { statusIsFetching, scanIsUnavailable, status } = useSelect( select => ( {
@@ -220,19 +175,13 @@ const ScanPage = () => {
const renderSection = useMemo( () => {
// Error
- if ( error || ( ! viewingScanHistory && scanIsUnavailable ) ) {
- return (
-
- );
+ if ( error || scanIsUnavailable ) {
+ return ;
}
// Scanning
const scanningStatuses = new Set( [ 'scheduled', 'scanning', 'optimistically_scanning' ] );
- if ( ! viewingScanHistory && ( scanningStatuses.has( status.status ) || ! lastChecked ) ) {
+ if ( scanningStatuses.has( status.status ) || ! lastChecked ) {
return ;
}
@@ -241,7 +190,6 @@ const ScanPage = () => {
error,
errorMessage,
errorCode,
- viewingScanHistory,
scanIsUnavailable,
status.status,
status.currentProgress,
diff --git a/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx b/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx
new file mode 100644
index 0000000000000..6418dba31727e
--- /dev/null
+++ b/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx
@@ -0,0 +1,46 @@
+import { Container, Col, Text, Title, getIconBySlug } from '@automattic/jetpack-components';
+import React from 'react';
+import ScanSectionNavigation from './scan-section-navigation';
+import styles from './styles.module.scss';
+
+type Props = {
+ title?: string | React.ReactNode;
+ subtitle?: string | React.ReactNode;
+ controls?: React.ReactNode;
+};
+
+const ScanSectionHeader = ( { title, subtitle, controls }: Props ) => {
+ const Icon = getIconBySlug( 'protect' );
+
+ return (
+
+
+
+
+ { subtitle && (
+
+
+ { subtitle }
+
+ ) }
+ { title && (
+
+ { title }
+
+ ) }
+
+
+
+
+
+
+ );
+};
+
+export default ScanSectionHeader;
diff --git a/projects/plugins/protect/src/js/routes/scan/scan-section-navigation.jsx b/projects/plugins/protect/src/js/routes/scan/scan-section-navigation.jsx
new file mode 100644
index 0000000000000..e34da434d43f5
--- /dev/null
+++ b/projects/plugins/protect/src/js/routes/scan/scan-section-navigation.jsx
@@ -0,0 +1,36 @@
+import { __ } from '@wordpress/i18n';
+import React, { useCallback } from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+import ButtonGroup from '../../components/button-group';
+
+/**
+ * Navigation for scan sections.
+ *
+ * @returns {React.Element} The React Component.
+ */
+export default function ScanSectionNavigation() {
+ const navigate = useNavigate();
+ const location = useLocation();
+
+ const navigateToScanPage = useCallback( () => navigate( '/scan' ), [ navigate ] );
+ const navigateToHistoryPage = useCallback( () => navigate( '/scan/history' ), [ navigate ] );
+
+ return (
+
+
+
+ { __( 'Scanner', 'jetpack-protect' ) }
+
+
+ { __( 'History', 'jetpack-protect' ) }
+
+
+
+ );
+}
diff --git a/projects/plugins/protect/src/js/routes/scan/styles.module.scss b/projects/plugins/protect/src/js/routes/scan/styles.module.scss
index bfbef2df43d0c..f48852795a657 100644
--- a/projects/plugins/protect/src/js/routes/scan/styles.module.scss
+++ b/projects/plugins/protect/src/js/routes/scan/styles.module.scss
@@ -28,3 +28,48 @@
justify-content: flex-end;
margin-top: calc( var( --spacing-base ) * 3 + 1px ); // 25px
}
+
+.scan-section-header {
+ > :first-child {
+ flex: 1;
+ }
+
+ @media ( min-width: 960px ) {
+ display: flex;
+ align-items: flex-start;
+ }
+}
+
+.scan-section-header__content {
+ display: flex;
+ flex-direction: column;
+ gap: var( --spacing-base ); // 8px
+
+ > :last-child {
+ margin-top: calc( var( --spacing-base ) * 2 ); // 16px
+ }
+}
+
+.scan-section-header__subtitle {
+ display: flex;
+ align-items: center;
+ color: var( --jp-black );
+ margin-bottom: 0;
+}
+
+.scan-section-header__icon {
+ margin-left: -4px; // this fix a blank space on right/left from @wordpress/icons
+ margin-right: var( --spacing-base ); // 8px
+}
+
+.scan-section-header__controls {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: calc( var( --spacing-base ) * 2 ); // 16px
+}
+
+.scan-section-header__controls__row {
+ display: flex;
+ gap: calc( var( --spacing-base ) * 2 ); // 16px
+}
diff --git a/projects/plugins/protect/src/js/state/actions.js b/projects/plugins/protect/src/js/state/actions.js
index adc4449c9b1f6..eb01caa8173f3 100644
--- a/projects/plugins/protect/src/js/state/actions.js
+++ b/projects/plugins/protect/src/js/state/actions.js
@@ -1,10 +1,10 @@
import apiFetch from '@wordpress/api-fetch';
import { sprintf, _n, __ } from '@wordpress/i18n';
import camelize from 'camelize';
+import API from '../api';
const SET_CREDENTIALS_STATE_IS_FETCHING = 'SET_CREDENTIALS_STATE_IS_FETCHING';
const SET_CREDENTIALS_STATE = 'SET_CREDENTIALS_STATE';
-const SET_VIEWING_SCAN_HISTORY = 'SET_VIEWING_SCAN_HISTORY';
const SET_SCAN_HISTORY = 'SET_SCAN_HISTORY';
const SET_STATUS = 'SET_STATUS';
const SET_STATUS_PROGRESS = 'SET_STATUS_PROGRESS';
@@ -33,10 +33,6 @@ const SET_WAF_IS_TOGGLING = 'SET_WAF_IS_TOGGLING';
const SET_WAF_CONFIG = 'SET_WAF_CONFIG';
const SET_WAF_STATS = 'SET_WAF_STATS';
-const setViewingScanHistory = viewingScanHistory => {
- return { type: SET_VIEWING_SCAN_HISTORY, viewingScanHistory };
-};
-
const setScanHistory = scanHistory => {
return { type: SET_SCAN_HISTORY, scanHistory };
};
@@ -101,6 +97,20 @@ const refreshStatus =
} );
};
+/**
+ * Refresh Scan History
+ * @returns {Promise} - Promise which resolves with the scan history once it has been fetched.
+ */
+const refreshScanHistory = () => {
+ return async ( { dispatch } ) => {
+ return API.fetchScanHistory()
+ .then( scanHistory => camelize( scanHistory ) )
+ .then( scanHistory => {
+ dispatch( setScanHistory( scanHistory ) );
+ } );
+ };
+};
+
/**
* Check Status
*
@@ -209,6 +219,9 @@ const ignoreThreat =
.then( () => {
return dispatch( refreshStatus() );
} )
+ .then( () => {
+ return dispatch( refreshScanHistory() );
+ } )
.then( () => {
return dispatch(
setNotice( { type: 'success', message: __( 'Threat ignored', 'jetpack-protect' ) } )
@@ -264,6 +277,7 @@ const getFixThreatsStatus =
.then( () => {
// threats fixed - refresh the status
dispatch( refreshStatus() );
+ dispatch( refreshScanHistory() );
dispatch(
setNotice( {
type: 'success',
@@ -428,12 +442,12 @@ const actions = {
checkCredentials,
setCredentials,
setCredentialsIsFetching,
- setViewingScanHistory,
setScanHistory,
setStatus,
setStatusProgress,
startScanOptimistically,
refreshStatus,
+ refreshScanHistory,
setStatusIsFetching,
setScanIsEnqueuing,
setInstalledPlugins,
@@ -463,7 +477,6 @@ const actions = {
export {
SET_CREDENTIALS_STATE,
SET_CREDENTIALS_STATE_IS_FETCHING,
- SET_VIEWING_SCAN_HISTORY,
SET_SCAN_HISTORY,
SET_STATUS,
SET_STATUS_PROGRESS,
diff --git a/projects/plugins/protect/src/js/state/reducers.js b/projects/plugins/protect/src/js/state/reducers.js
index 30586bca084c6..8d4e8bb39a6d4 100644
--- a/projects/plugins/protect/src/js/state/reducers.js
+++ b/projects/plugins/protect/src/js/state/reducers.js
@@ -1,8 +1,8 @@
import { combineReducers } from '@wordpress/data';
+import camelize from 'camelize';
import {
SET_CREDENTIALS_STATE,
SET_CREDENTIALS_STATE_IS_FETCHING,
- SET_VIEWING_SCAN_HISTORY,
SET_SCAN_HISTORY,
SET_STATUS,
SET_STATUS_PROGRESS,
@@ -46,18 +46,10 @@ const credentialsIsFetching = ( state = false, action ) => {
return state;
};
-const viewingScanHistory = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_VIEWING_SCAN_HISTORY:
- return action.viewingScanHistory;
- }
- return state;
-};
-
const scanHistory = ( state = {}, action ) => {
switch ( action.type ) {
case SET_SCAN_HISTORY:
- return action.scanHistory;
+ return camelize( action.scanHistory );
}
return state;
};
@@ -214,7 +206,6 @@ const waf = ( state = defaultWaf, action ) => {
const reducers = combineReducers( {
credentials,
credentialsIsFetching,
- viewingScanHistory,
scanHistory,
status,
statusIsFetching,
diff --git a/projects/plugins/protect/src/js/state/selectors.js b/projects/plugins/protect/src/js/state/selectors.js
index 7f6ddda7501e8..9a8e818f4e82c 100644
--- a/projects/plugins/protect/src/js/state/selectors.js
+++ b/projects/plugins/protect/src/js/state/selectors.js
@@ -3,7 +3,6 @@ const selectors = {
getCredentialsIsFetching: state => state.credentialsIsFetching || false,
getInstalledPlugins: state => state.installedPlugins || {},
getInstalledThemes: state => state.installedThemes || {},
- getViewingScanHistory: state => state.viewingScanHistory || false,
getScanHistory: state => state.scanHistory || {},
getStatus: state => state.status || {},
getStatusIsFetching: state => state.statusIsFetching || false,
diff --git a/projects/plugins/protect/src/js/styles.module.scss b/projects/plugins/protect/src/js/styles.module.scss
index 5e314bd417fb7..4d7ac08500360 100644
--- a/projects/plugins/protect/src/js/styles.module.scss
+++ b/projects/plugins/protect/src/js/styles.module.scss
@@ -2,4 +2,13 @@
* {
box-sizing: border-box;
}
+
+ #jetpack-protect-root {
+ --wp-admin-theme-color: var(--jp-black);
+ --wp-admin-theme-color-darker-10: var(--jp-black-80);
+ --wp-admin-theme-color-darker-20: var(--jp-black-80);
+ // 1.51px to avoid subpixel rendering issues on Firefox/Linux
+ --wp-admin-border-width-focus: 1.51px;
+ --actions-size: 28px;
+ }
}
diff --git a/projects/plugins/protect/src/models/class-history-model.php b/projects/plugins/protect/src/models/class-history-model.php
index 040d652f696f7..1f354fceecab8 100644
--- a/projects/plugins/protect/src/models/class-history-model.php
+++ b/projects/plugins/protect/src/models/class-history-model.php
@@ -18,13 +18,6 @@ class History_Model {
*/
public $last_checked;
- /**
- * The filter to apply to the history.
- *
- * @var array
- */
- public $filter = array( 'ignored', 'fixed' );
-
/**
* The number of threats.
*