diff --git a/client/lib/url/support.js b/client/lib/url/support.js index 2ffab16d9caf7..722ba6a0f9405 100644 --- a/client/lib/url/support.js +++ b/client/lib/url/support.js @@ -5,6 +5,7 @@ export const ADDING_TITAN_TO_YOUR_SITE = `${ root }/add-email/adding-professiona export const AUTO_RENEWAL = `${ root }/manage-purchases/#automatic-renewal`; export const CHANGE_NAME_SERVERS = `${ root }/domains/change-name-servers/`; export const CHANGE_NAME_SERVERS_FINDING_OUT_NEW_NS = `${ root }/domains/change-name-servers/#find-your-new-name-servers`; +export const CLEAR_CACHE = `${ root }/clear-your-sites-cache/`; export const CONCIERGE_SUPPORT = `${ root }/concierge-support/`; export const CONTACT = `${ root }/contact/`; export const CALYPSO_CONTACT = '/help/contact'; diff --git a/client/my-sites/hosting/cache-card/index.js b/client/my-sites/hosting/cache-card/index.js new file mode 100644 index 0000000000000..4f05cbd1adbb6 --- /dev/null +++ b/client/my-sites/hosting/cache-card/index.js @@ -0,0 +1,80 @@ +import { Button, Card } from '@automattic/components'; +import { localizeUrl } from '@automattic/i18n-utils'; +import { localize } from 'i18n-calypso'; +import { connect } from 'react-redux'; +import CardHeading from 'calypso/components/card-heading'; +import ExternalLink from 'calypso/components/external-link'; +import MaterialIcon from 'calypso/components/material-icon'; +import { CLEAR_CACHE } from 'calypso/lib/url/support'; +import { clearWordPressCache } from 'calypso/state/hosting/actions'; +import getRequest from 'calypso/state/selectors/get-request'; +import { shouldRateLimitAtomicCacheClear } from 'calypso/state/selectors/should-rate-limit-atomic-cache-clear'; +import { getSelectedSiteId } from 'calypso/state/ui/selectors'; + +import './style.scss'; + +const CacheCard = ( { + disabled, + shouldRateLimitCacheClear, + clearAtomicWordPressCache, + isClearingCache, + siteId, + translate, +} ) => { + const clearCache = () => { + clearAtomicWordPressCache( siteId, 'Manually clearing again.' ); + }; + + const getClearCacheContent = () => { + return ( +
+

+ { translate( + 'Be careful, clearing the cache may make your site unresponsive while it is being rebuilt. {{a}}Learn more about clearing your site’s cache{{/a}}', + { + components: { + a: , + }, + } + ) } +

+ + { shouldRateLimitCacheClear && ( +

+ { translate( 'You cleared the cache recently. Please wait a minute and try again.' ) } +

+ ) } +
+ ); + }; + //autorenew + return ( + + + { translate( 'Cache' ) } + { getClearCacheContent() } + + ); +}; + +export default connect( + ( state ) => { + const siteId = getSelectedSiteId( state ); + + return { + shouldRateLimitCacheClear: shouldRateLimitAtomicCacheClear( state, siteId ), + isClearingCache: getRequest( state, clearWordPressCache( siteId ) )?.isLoading ?? false, + siteId, + }; + }, + { + clearAtomicWordPressCache: clearWordPressCache, + } +)( localize( CacheCard ) ); diff --git a/client/my-sites/hosting/miscellaneous-card/style.scss b/client/my-sites/hosting/cache-card/style.scss similarity index 100% rename from client/my-sites/hosting/miscellaneous-card/style.scss rename to client/my-sites/hosting/cache-card/style.scss diff --git a/client/my-sites/hosting/main.js b/client/my-sites/hosting/main.js index c9ec62d407ec3..7581338db6d87 100644 --- a/client/my-sites/hosting/main.js +++ b/client/my-sites/hosting/main.js @@ -31,8 +31,8 @@ import siteHasFeature from 'calypso/state/selectors/site-has-feature'; import { requestSite } from 'calypso/state/sites/actions'; import { isSiteOnECommerceTrial } from 'calypso/state/sites/plans/selectors'; import { getSelectedSiteId, getSelectedSiteSlug } from 'calypso/state/ui/selectors'; +import CacheCard from './cache-card'; import { HostingUpsellNudge } from './hosting-upsell-nudge'; -import MiscellaneousCard from './miscellaneous-card'; import PhpMyAdminCard from './phpmyadmin-card'; import RestorePlanSoftwareCard from './restore-plan-software-card'; import SFTPCard from './sftp-card'; @@ -184,7 +184,7 @@ class Hosting extends Component { { isGitHubEnabled && } - + diff --git a/client/my-sites/hosting/miscellaneous-card/index.js b/client/my-sites/hosting/miscellaneous-card/index.js deleted file mode 100644 index be54fce7f4adc..0000000000000 --- a/client/my-sites/hosting/miscellaneous-card/index.js +++ /dev/null @@ -1,141 +0,0 @@ -import { Button, Card, Dialog } from '@automattic/components'; -import { localize } from 'i18n-calypso'; -import { useState } from 'react'; -import { connect } from 'react-redux'; -import CardHeading from 'calypso/components/card-heading'; -import FormLabel from 'calypso/components/forms/form-label'; -import FormTextInput from 'calypso/components/forms/form-text-input'; -import MaterialIcon from 'calypso/components/material-icon'; -import { clearWordPressCache } from 'calypso/state/hosting/actions'; -import getRequest from 'calypso/state/selectors/get-request'; -import { isAtomicClearCacheEnabled } from 'calypso/state/selectors/is-atomic-clear-cache-enabled'; -import { shouldProvideReasonToClearAtomicCache } from 'calypso/state/selectors/should-provide-reason-to-clear-atomic-cache'; -import { getSelectedSiteId } from 'calypso/state/ui/selectors'; - -import './style.scss'; - -const MiscellaneousCard = ( { - disabled, - shouldProvideReasonToClearCache, - clearAtomicWordPressCache, - isClearCacheEnabled, - isClearingCache, - siteId, - translate, -} ) => { - const [ showDialog, setShowDialog ] = useState( false ); - const [ reason, setReason ] = useState( '' ); - - const clearCache = () => { - clearAtomicWordPressCache( - siteId, - shouldProvideReasonToClearCache ? reason : 'Clearing again in less than 24 hours.' - ); - setShowDialog( false ); - setReason( '' ); - }; - - const showConfirmationDialog = () => { - setShowDialog( true ); - }; - - const closeConfirmationDialog = () => { - setShowDialog( false ); - }; - - const onReasonChange = ( event ) => { - setReason( event.target.value ); - }; - - const getClearCacheContent = () => { - const clearCacheText = translate( 'Clear Cache' ); - const clearCacheDisabled = ! reason || reason.length < 3; - const deleteButtons = [ - , - , - ]; - - return ( -
-

- { translate( '{{strong}}Warning!{{/strong}}', { - components: { - strong: , - }, - } ) } -

-

- { translate( - 'Clearing the cache on your site may make it unresponsive while the cache is rebuilding. ' + - 'Please use this feature responsibly.' - ) } -

- - - -

{ clearCacheText }

- -

{ translate( "Please let us know why you are clearing your site's cache." ) }

-

- { translate( - 'We use this information to audit plugins and give valuable feedback to plugin developers. ' + - 'It is our way of giving back to the community and helping people learn more about WordPress.' - ) } -

-
- - -
-
- ); - }; - - if ( ! isClearCacheEnabled ) { - return null; - } - - return ( - - - { translate( 'Miscellaneous' ) } - { getClearCacheContent() } - - ); -}; - -export default connect( - ( state ) => { - const siteId = getSelectedSiteId( state ); - - return { - shouldProvideReasonToClearCache: shouldProvideReasonToClearAtomicCache( state, siteId ), - isClearCacheEnabled: isAtomicClearCacheEnabled( state, siteId ), - isClearingCache: getRequest( state, clearWordPressCache( siteId ) )?.isLoading ?? false, - siteId, - }; - }, - { - clearAtomicWordPressCache: clearWordPressCache, - } -)( localize( MiscellaneousCard ) ); diff --git a/client/state/selectors/is-atomic-clear-cache-enabled.js b/client/state/selectors/is-atomic-clear-cache-enabled.js deleted file mode 100644 index 2f2fd2fb866aa..0000000000000 --- a/client/state/selectors/is-atomic-clear-cache-enabled.js +++ /dev/null @@ -1,6 +0,0 @@ -import { currentUserHasFlag } from 'calypso/state/current-user/selectors'; -import { isSupportSession } from 'calypso/state/support/selectors'; - -export function isAtomicClearCacheEnabled( state ) { - return currentUserHasFlag( state, 'calypso_atomic_clear_cache' ) || isSupportSession( state ); -} diff --git a/client/state/selectors/should-provide-reason-to-clear-atomic-cache.js b/client/state/selectors/should-provide-reason-to-clear-atomic-cache.js deleted file mode 100644 index 2a9ebfced3f6b..0000000000000 --- a/client/state/selectors/should-provide-reason-to-clear-atomic-cache.js +++ /dev/null @@ -1,16 +0,0 @@ -import 'calypso/state/hosting/init'; - -const ONE_DAY_IN_MILLISECONDS = 86400 * 1000; - -export function shouldProvideReasonToClearAtomicCache( state, siteId ) { - const lastCacheCleared = state.atomicHosting?.[ siteId ]?.lastCacheClearTimestamp; - - if ( ! lastCacheCleared ) { - return true; - } - - const oneDayAgo = new Date().valueOf() - ONE_DAY_IN_MILLISECONDS; - const wasCleanedMoreThanOneDayAgo = oneDayAgo > lastCacheCleared; - - return wasCleanedMoreThanOneDayAgo; -} diff --git a/client/state/selectors/should-rate-limit-atomic-cache-clear.js b/client/state/selectors/should-rate-limit-atomic-cache-clear.js new file mode 100644 index 0000000000000..315cf6e4e5ace --- /dev/null +++ b/client/state/selectors/should-rate-limit-atomic-cache-clear.js @@ -0,0 +1,14 @@ +import 'calypso/state/hosting/init'; + +const ONE_MINUTE_IN_MILLISECONDS = 60 * 1000; + +export function shouldRateLimitAtomicCacheClear( state, siteId ) { + const lastCacheCleared = state.atomicHosting?.[ siteId ]?.lastCacheClearTimestamp; + + if ( ! lastCacheCleared ) { + return false; + } + + const rateLimitTime = new Date().valueOf() - ONE_MINUTE_IN_MILLISECONDS; + return lastCacheCleared > rateLimitTime; +} diff --git a/client/state/selectors/test/should-provide-reason-to-clear-atomic-cache.js b/client/state/selectors/test/should-provide-reason-to-clear-atomic-cache.js deleted file mode 100644 index b92bbab7706af..0000000000000 --- a/client/state/selectors/test/should-provide-reason-to-clear-atomic-cache.js +++ /dev/null @@ -1,48 +0,0 @@ -import { shouldProvideReasonToClearAtomicCache } from 'calypso/state/selectors/should-provide-reason-to-clear-atomic-cache'; - -const ONE_DAY_IN_MILLISECONDS = 86400 * 1000; -const ONE_SECOND = 1000; -const SITE_ID = 123; -const TIMESTAMP = 1234567890; - -const generateState = ( { timestamp } ) => ( { - atomicHosting: { - [ SITE_ID ]: { - lastCacheClearTimestamp: timestamp, - }, - }, -} ); - -describe( 'shouldProvideReasonToClearAtomicCache', () => { - beforeAll( () => { - jest.useFakeTimers( 'modern' ).setSystemTime( TIMESTAMP ); - } ); - - afterAll( () => { - jest.useRealTimers(); - } ); - - test( 'should return true if there is no stored timestamp', () => { - expect( - shouldProvideReasonToClearAtomicCache( generateState( { timestamp: null } ), SITE_ID ) - ).toBe( true ); - } ); - - test( 'should return true if the cache was cleared more than 24 hours ago', () => { - expect( - shouldProvideReasonToClearAtomicCache( - generateState( { timestamp: TIMESTAMP - ONE_DAY_IN_MILLISECONDS - ONE_SECOND } ), - SITE_ID - ) - ).toBe( true ); - } ); - - test( 'should return false if the cache was cleared less than 24 hours ago', () => { - expect( - shouldProvideReasonToClearAtomicCache( - generateState( { timestamp: TIMESTAMP - ONE_DAY_IN_MILLISECONDS } ), - SITE_ID - ) - ).toBe( false ); - } ); -} ); diff --git a/client/state/selectors/test/should-rate-limit-atomic-cache-clear.js b/client/state/selectors/test/should-rate-limit-atomic-cache-clear.js new file mode 100644 index 0000000000000..f97bef716363f --- /dev/null +++ b/client/state/selectors/test/should-rate-limit-atomic-cache-clear.js @@ -0,0 +1,48 @@ +import { shouldRateLimitAtomicCacheClear } from 'calypso/state/selectors/should-rate-limit-atomic-cache-clear'; + +const ONE_MINUTE_IN_MILLISECONDS = 60 * 1000; +const ONE_SECOND = 1000; +const SITE_ID = 123; +const TIMESTAMP = 1234567890; + +const generateState = ( { timestamp } ) => ( { + atomicHosting: { + [ SITE_ID ]: { + lastCacheClearTimestamp: timestamp, + }, + }, +} ); + +describe( 'shouldRateLimitAtomicCacheClear', () => { + beforeAll( () => { + jest.useFakeTimers( 'modern' ).setSystemTime( TIMESTAMP ); + } ); + + afterAll( () => { + jest.useRealTimers(); + } ); + + test( 'should return false if there is no stored timestamp', () => { + expect( shouldRateLimitAtomicCacheClear( generateState( { timestamp: null } ), SITE_ID ) ).toBe( + false + ); + } ); + + test( 'should return false if the cache was cleared more than a minute ago', () => { + expect( + shouldRateLimitAtomicCacheClear( + generateState( { timestamp: TIMESTAMP - ONE_MINUTE_IN_MILLISECONDS - ONE_SECOND } ), + SITE_ID + ) + ).toBe( false ); + } ); + + test( 'should return true if the cache was cleared less than a minute ago', () => { + expect( + shouldRateLimitAtomicCacheClear( + generateState( { timestamp: TIMESTAMP - ONE_MINUTE_IN_MILLISECONDS + ONE_SECOND } ), + SITE_ID + ) + ).toBe( true ); + } ); +} );