diff --git a/client/components/premium-global-styles-upgrade-modal/index.tsx b/client/components/premium-global-styles-upgrade-modal/index.tsx index 3676d2a05b8bf..046b34b2a0ace 100644 --- a/client/components/premium-global-styles-upgrade-modal/index.tsx +++ b/client/components/premium-global-styles-upgrade-modal/index.tsx @@ -1,4 +1,3 @@ -import { isEnabled } from '@automattic/calypso-config'; import { PLAN_PERSONAL, PLAN_PREMIUM } from '@automattic/calypso-products'; import { Button, Gridicon, Dialog, ScreenReaderText, PlanPrice } from '@automattic/components'; import { Plans } from '@automattic/data-stores'; @@ -10,6 +9,7 @@ import { LoadingEllipsis } from 'calypso/components/loading-ellipsis'; import useCheckPlanAvailabilityForPurchase from 'calypso/my-sites/plans-features-main/hooks/use-check-plan-availability-for-purchase'; import { useSelector } from 'calypso/state'; import { getProductBySlug } from 'calypso/state/products-list/selectors'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; import { getSelectedSiteId } from 'calypso/state/ui/selectors'; import useGlobalStylesUpgradeTranslations from './use-global-styles-upgrade-translations'; import './style.scss'; @@ -34,9 +34,7 @@ export default function PremiumGlobalStylesUpgradeModal( { }: PremiumGlobalStylesUpgradeModalProps ) { const translate = useTranslate(); // @TODO Cleanup once the test phase is over. - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) - ? PLAN_PERSONAL - : PLAN_PREMIUM; + const upgradeToPlan = useSiteGlobalStylesOnPersonal() ? PLAN_PERSONAL : PLAN_PREMIUM; const premiumPlanProduct = useSelector( ( state ) => getProductBySlug( state, upgradeToPlan ) ); const selectedSiteId = useSelector( getSelectedSiteId ); const translations = useGlobalStylesUpgradeTranslations( { numOfSelectedGlobalStyles } ); diff --git a/client/components/premium-global-styles-upgrade-modal/use-global-styles-upgrade-translations.tsx b/client/components/premium-global-styles-upgrade-modal/use-global-styles-upgrade-translations.tsx index 3d302081c41c3..746f335fb8106 100644 --- a/client/components/premium-global-styles-upgrade-modal/use-global-styles-upgrade-translations.tsx +++ b/client/components/premium-global-styles-upgrade-modal/use-global-styles-upgrade-translations.tsx @@ -1,8 +1,8 @@ -import { isEnabled } from '@automattic/calypso-config'; import { PLAN_PERSONAL, PLAN_PREMIUM } from '@automattic/calypso-products'; import { Plans } from '@automattic/data-stores'; import { useHasEnTranslation } from '@automattic/i18n-utils'; import { useTranslate } from 'i18n-calypso'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; interface Props { numOfSelectedGlobalStyles?: number; @@ -14,9 +14,7 @@ const useGlobalStylesUpgradeTranslations = ( { numOfSelectedGlobalStyles = 1 }: const plans = Plans.usePlans( { coupon: undefined } ); // @TODO Cleanup once the test phase is over. - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) - ? PLAN_PERSONAL - : PLAN_PREMIUM; + const upgradeToPlan = useSiteGlobalStylesOnPersonal() ? PLAN_PERSONAL : PLAN_PREMIUM; const planTitle = plans?.data?.[ upgradeToPlan ]?.productNameShort ?? ''; @@ -43,7 +41,7 @@ const useGlobalStylesUpgradeTranslations = ( { numOfSelectedGlobalStyles = 1 }: featuresTitle: translate( 'Included with your %(planTitle)s plan', { args: { planTitle }, } ), - features: isEnabled( 'global-styles/on-personal-plan' ) ? personalFeatures : premiumFeatures, + features: useSiteGlobalStylesOnPersonal() ? personalFeatures : premiumFeatures, description: translate( 'You’ve selected a premium style that will only be visible to visitors after upgrading to the %(planTitle)s plan or higher.', 'You’ve selected premium styles that will only be visible to visitors after upgrading to the %(planTitle)s plan or higher.', diff --git a/client/components/theme-tier/constants.js b/client/components/theme-tier/constants.js index 0486b8489e1f7..83ca349868902 100644 --- a/client/components/theme-tier/constants.js +++ b/client/components/theme-tier/constants.js @@ -15,6 +15,7 @@ const getIncludedWithLabel = ( planSlug ) => { export const THEME_TIER_PREMIUM = 'premium'; export const THEME_TIER_PARTNER = 'partner'; +export const THEME_TIER_FREE = 'free'; /** * @typedef {Object} THEME_TIERS diff --git a/client/components/theme-tier/theme-tier-badge/test/theme-tier-style-variation-badge.js b/client/components/theme-tier/theme-tier-badge/test/theme-tier-style-variation-badge.js index 9814b2e4f0589..763bf9d46d4e3 100644 --- a/client/components/theme-tier/theme-tier-badge/test/theme-tier-style-variation-badge.js +++ b/client/components/theme-tier/theme-tier-badge/test/theme-tier-style-variation-badge.js @@ -1,5 +1,6 @@ /** @jest-environment jsdom */ import { getPlan } from '@automattic/calypso-products'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useSelector } from 'calypso/state'; @@ -12,6 +13,23 @@ describe( 'ThemeTierStyleVariationBadge', () => { const siteSlug = 'example.wordpress.com'; let originalWindowLocation; + // Create a QueryClient instance + const createTestQueryClient = () => + new QueryClient( { + defaultOptions: { + queries: { + retry: false, // Disable retries for tests + cacheTime: 0, // Disable cache + }, + }, + } ); + + // Utility to wrap component with QueryClientProvider + const renderWithQueryClient = ( ui ) => { + const queryClient = createTestQueryClient(); + return render( { ui } ); + }; + beforeEach( () => { jest.clearAllMocks(); @@ -30,7 +48,7 @@ describe( 'ThemeTierStyleVariationBadge', () => { } ); test( 'should render upgrade label', () => { - render( ); + renderWithQueryClient( ); const upgradeLabel = screen.getByText( 'Upgrade' ); expect( upgradeLabel ).toBeInTheDocument(); @@ -44,7 +62,7 @@ describe( 'ThemeTierStyleVariationBadge', () => { getPathSlug: () => pathSlug, } ) ); - render( ); + renderWithQueryClient( ); userEvent.hover( screen.getByText( 'Upgrade' ) ); diff --git a/client/components/theme-tier/theme-tier-badge/theme-tier-style-variation-badge.js b/client/components/theme-tier/theme-tier-badge/theme-tier-style-variation-badge.js index bd2ddba78c29f..fcc546f425242 100644 --- a/client/components/theme-tier/theme-tier-badge/theme-tier-style-variation-badge.js +++ b/client/components/theme-tier/theme-tier-badge/theme-tier-style-variation-badge.js @@ -1,8 +1,8 @@ -import { isEnabled } from '@automattic/calypso-config'; import { PLAN_PREMIUM, getPlan, PLAN_PERSONAL } from '@automattic/calypso-products'; import { PremiumBadge } from '@automattic/components'; import { createInterpolateElement } from '@wordpress/element'; import { useTranslate } from 'i18n-calypso'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; import ThemeTierBadgeCheckoutLink from './theme-tier-badge-checkout-link'; import ThemeTierTooltipTracker from './theme-tier-tooltip-tracker'; @@ -10,7 +10,7 @@ export default function ThemeTierStyleVariationBadge() { const translate = useTranslate(); // @TODO Cleanup once the test phase is over. - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) + const upgradeToPlan = useSiteGlobalStylesOnPersonal() ? getPlan( PLAN_PERSONAL ) : getPlan( PLAN_PREMIUM ); diff --git a/client/jetpack-cloud/sections/partner-portal/primary/wpcom-atomic-hosting/card-content.tsx b/client/jetpack-cloud/sections/partner-portal/primary/wpcom-atomic-hosting/card-content.tsx index 9c668980d9ea6..7f716c99659d7 100644 --- a/client/jetpack-cloud/sections/partner-portal/primary/wpcom-atomic-hosting/card-content.tsx +++ b/client/jetpack-cloud/sections/partner-portal/primary/wpcom-atomic-hosting/card-content.tsx @@ -18,6 +18,7 @@ import { recordTracksEvent } from 'calypso/state/analytics/actions'; import { infoNotice } from 'calypso/state/notices/actions'; import useProductsQuery from 'calypso/state/partner-portal/licenses/hooks/use-products-query'; import { doesPartnerRequireAPaymentMethod } from 'calypso/state/partner-portal/partner/selectors'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; import FeatureItem from './feature-item'; import './style.scss'; @@ -48,6 +49,9 @@ export default function CardContent( { const { data: agencyProducts } = useProductsQuery(); const paymentMethodRequired = useSelector( doesPartnerRequireAPaymentMethod ); + // Set a prop on the window object on whether Global Styles is available on the Personal plan. + useSiteGlobalStylesOnPersonal(); + const getLogo = ( planSlug: string ) => { switch ( planSlug ) { case PLAN_BUSINESS: diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/test/unified-design-picker.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/test/unified-design-picker.tsx index 9ef6c43dfc6a2..71665cc08d455 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/test/unified-design-picker.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/test/unified-design-picker.tsx @@ -117,6 +117,7 @@ const renderComponent = ( component, initialState = {} ) => { const store = mockStore( { purchases: {}, sites: {}, + ui: { selectedSiteId: 'anySiteId' }, ...initialState, } ); diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx index a71719a952965..95d334c6525fe 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx @@ -1,4 +1,3 @@ -import { isEnabled } from '@automattic/calypso-config'; import { TERM_ANNUALLY, TERM_MONTHLY, @@ -41,6 +40,7 @@ import { THEME_TIERS, THEME_TIER_PARTNER, THEME_TIER_PREMIUM, + THEME_TIER_FREE, } from 'calypso/components/theme-tier/constants'; import ThemeTierBadge from 'calypso/components/theme-tier/theme-tier-badge'; import { ThemeUpgradeModal as UpgradeModal } from 'calypso/components/theme-upgrade-modal'; @@ -57,6 +57,7 @@ import { getProductsByBillingSlug, } from 'calypso/state/products-list/selectors'; import { hasPurchasedDomain } from 'calypso/state/purchases/selectors/has-purchased-domain'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; import { useSiteGlobalStylesStatus } from 'calypso/state/sites/hooks/use-site-global-styles-status'; import { getSiteSlug } from 'calypso/state/sites/selectors'; import { setActiveTheme, activateOrInstallThenActivate } from 'calypso/state/themes/actions'; @@ -144,6 +145,8 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { const siteDescription = site?.description; const { shouldLimitGlobalStyles } = useSiteGlobalStylesStatus( site?.ID ); const { data: siteActiveTheme } = useActiveThemeQuery( site?.ID ?? 0, !! site?.ID ); + // @TODO Cleanup once the test phase is over. + const isGlobalStylesOnPersonal = useSiteGlobalStylesOnPersonal( site?.ID ); const isDesignFirstFlow = flow === DESIGN_FIRST_FLOW || queryParams.get( 'flowToReturnTo' ) === DESIGN_FIRST_FLOW; @@ -581,16 +584,13 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { } ) ); - // @TODO Cleanup once the test phase is over. - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) ? 'personal' : 'premium'; - goToCheckout( { flowName: flow, stepName, siteSlug: siteSlug || urlToSlug( site?.URL || '' ) || '', // When the user is done with checkout, send them back to the current url destination: window.location.href.replace( window.location.origin, '' ), - plan: upgradeToPlan, + plan: isGlobalStylesOnPersonal ? 'personal' : 'premium', } ); setShowPremiumGlobalStylesModal( false ); @@ -736,7 +736,7 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { } ); } function getPrimaryActionButtonAction(): () => void { - if ( isEnabled( 'global-styles/on-personal-plan' ) ) { + if ( isGlobalStylesOnPersonal ) { if ( isLockedTheme ) { return upgradePlan; } @@ -841,12 +841,16 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { placeholder={ null } previewUrl={ previewUrl } splitDefaultVariation={ - ! ( selectedDesign?.design_tier === THEME_TIER_PREMIUM ) && - ! isBundled && - ! isPremiumThemeAvailable && - ! didPurchaseSelectedTheme && - ! isPluginBundleEligible && - shouldLimitGlobalStyles + ( isGlobalStylesOnPersonal && + selectedDesign?.design_tier === THEME_TIER_FREE && + shouldLimitGlobalStyles ) || + ( ! ( selectedDesign?.design_tier === THEME_TIER_PREMIUM ) && + ! isBundled && + ! isPremiumThemeAvailable && + ! didPurchaseSelectedTheme && + ! isPluginBundleEligible && + ! isGlobalStylesOnPersonal && + shouldLimitGlobalStyles ) } needsUpgrade={ shouldLimitGlobalStyles || isLockedTheme } title={ headerDesignTitle } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/sidebar.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/sidebar.tsx index 536870e31898c..3d539632a3b66 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/sidebar.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/sidebar.tsx @@ -1,4 +1,4 @@ -import { PLAN_PREMIUM } from '@automattic/calypso-products'; +import { PLAN_PERSONAL, PLAN_PREMIUM } from '@automattic/calypso-products'; import { Badge, CircularProgressBar, Gridicon, Tooltip } from '@automattic/components'; import { OnboardSelect, @@ -25,6 +25,7 @@ import { TYPE_TIER } from 'calypso/my-sites/earn/memberships/constants'; import { useSelector } from 'calypso/state'; import { isCurrentUserEmailVerified } from 'calypso/state/current-user/selectors'; import { getConnectUrlForSiteId } from 'calypso/state/memberships/settings/selectors'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; import { useSiteGlobalStylesStatus } from 'calypso/state/sites/hooks/use-site-global-styles-status'; import { getEnhancedTasks } from './task-definitions'; import { getLaunchpadTranslations } from './translations'; @@ -107,6 +108,9 @@ const Sidebar = ( { ); const displayGlobalStylesWarning = globalStylesInUse && shouldLimitGlobalStyles; + const globalStylesMinimumPlan = useSiteGlobalStylesOnPersonal( site?.ID ) + ? PLAN_PERSONAL + : PLAN_PREMIUM; let checklist = launchpadChecklist; if ( selectedDesign?.default ) { @@ -129,7 +133,7 @@ const Sidebar = ( { site, submit, displayGlobalStylesWarning, - globalStylesMinimumPlan: PLAN_PREMIUM, + globalStylesMinimumPlan, setShowPlansModal, queryClient, goToStep, diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/task-definitions/plan/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/task-definitions/plan/index.tsx index d0a21ea7c874f..177c3a5abd5cf 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/task-definitions/plan/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/task-definitions/plan/index.tsx @@ -1,9 +1,4 @@ -import { isEnabled } from '@automattic/calypso-config'; -import { - FEATURE_STYLE_CUSTOMIZATION, - isFreePlanProduct, - PLAN_PERSONAL, -} from '@automattic/calypso-products'; +import { FEATURE_STYLE_CUSTOMIZATION, isFreePlanProduct } from '@automattic/calypso-products'; import { updateLaunchpadSettings } from '@automattic/data-stores/src/queries/use-launchpad'; import { localizeUrl } from '@automattic/i18n-utils'; import { Task } from '@automattic/launchpad'; @@ -54,13 +49,6 @@ export const getPlanSelectedTask: TaskAction = ( task, flow, context ): Task => const { siteSlug, displayGlobalStylesWarning, globalStylesMinimumPlan, hasSkippedCheckout } = context; - // @TODO Cleanup once the test phase is over. - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) - ? PLAN_PERSONAL - : globalStylesMinimumPlan; - - const shouldDisplayWarning = displayGlobalStylesWarning; - return { ...task, actionDispatch: () => { @@ -71,8 +59,8 @@ export const getPlanSelectedTask: TaskAction = ( task, flow, context ): Task => } }, calypso_path: addQueryArgs( `/plans/${ siteSlug }`, { - ...( shouldDisplayWarning && { - plan: upgradeToPlan, + ...( displayGlobalStylesWarning && { + plan: globalStylesMinimumPlan, feature: FEATURE_STYLE_CUSTOMIZATION, } ), } ), diff --git a/client/my-sites/theme/main.jsx b/client/my-sites/theme/main.jsx index 004d596c1878d..5d57d98f6321c 100644 --- a/client/my-sites/theme/main.jsx +++ b/client/my-sites/theme/main.jsx @@ -1,5 +1,5 @@ import { getTracksAnonymousUserId } from '@automattic/calypso-analytics'; -import config, { isEnabled } from '@automattic/calypso-config'; +import config from '@automattic/calypso-config'; import { FEATURE_UPLOAD_THEMES, PLAN_BUSINESS, @@ -66,6 +66,7 @@ import isSiteWPForTeams from 'calypso/state/selectors/is-site-wpforteams'; import isVipSite from 'calypso/state/selectors/is-vip-site'; import siteHasFeature from 'calypso/state/selectors/site-has-feature'; import { useSiteGlobalStylesStatus } from 'calypso/state/sites/hooks/use-site-global-styles-status'; +import { withSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/with-site-global-styles-on-personal'; import { getCurrentPlan, isSiteOnECommerceTrial } from 'calypso/state/sites/plans/selectors'; import { getSiteSlug, isJetpackSite } from 'calypso/state/sites/selectors'; import { @@ -677,28 +678,28 @@ class ThemeSheet extends Component { isBundledSoftwareSet, } = this.props; - const isGlobalStylesEnabled = isEnabled( 'global-styles/on-personal-plan' ); + const isGlobalStylesOnPersonal = this.props.isGlobalStylesOnPersonal; const isFreeTier = isFreePlan && themeTier?.slug === 'free'; const hasLimitedFeatures = ! isExternallyManagedTheme && ! isBundledSoftwareSet && ! isThemePurchased && - ! isGlobalStylesEnabled && + ! isGlobalStylesOnPersonal && ! isPremium && shouldLimitGlobalStyles; const shouldSplitDefaultVariation = isFreeTier || hasLimitedFeatures; - const needsUpgrade = isGlobalStylesEnabled - ? isFreePlan + const needsUpgrade = isGlobalStylesOnPersonal + ? isFreePlan || shouldLimitGlobalStyles : shouldLimitGlobalStyles || ( isPremium && ! isThemePurchased ); return ( styleVariations.length > 0 && ( { + const isGlobalStylesOnPersonal = useSiteGlobalStylesOnPersonal(); return (
{ !! description &&

{ description }

} @@ -35,6 +37,7 @@ const ThemeStyleVariations = ( { showOnlyHoverViewDefaultVariation={ false } needsUpgrade={ needsUpgrade } onSelect={ onClick } + isGlobalStylesOnPersonal={ isGlobalStylesOnPersonal } />
diff --git a/client/my-sites/themes/theme-options.js b/client/my-sites/themes/theme-options.js index 6b85131f140e1..d076c14094e19 100644 --- a/client/my-sites/themes/theme-options.js +++ b/client/my-sites/themes/theme-options.js @@ -1,4 +1,3 @@ -import { isEnabled } from '@automattic/calypso-config'; import { WPCOM_FEATURES_INSTALL_PLUGINS, PLAN_PERSONAL, @@ -25,6 +24,7 @@ import getCustomizeUrl from 'calypso/state/selectors/get-customize-url'; import isSiteWpcomAtomic from 'calypso/state/selectors/is-site-wpcom-atomic'; import isSiteWpcomStaging from 'calypso/state/selectors/is-site-wpcom-staging'; import siteHasFeature from 'calypso/state/selectors/site-has-feature'; +import { withSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/with-site-global-styles-on-personal'; import { isJetpackSite, isJetpackSiteMultiSite, @@ -74,7 +74,7 @@ function getPlanPathSlugForThemes( state, siteId, minimumPlan ) { return mappedPlan?.getPathSlug(); } -function getAllThemeOptions( { translate, isFSEActive } ) { +function getAllThemeOptions( { translate, isFSEActive, isGlobalStylesOnPersonal } ) { const purchase = { label: translate( 'Purchase', { context: 'verb', @@ -102,7 +102,7 @@ function getAllThemeOptions( { translate, isFSEActive } ) { // @TODO Cleanup once the test phase is over. let minimumPlan; - if ( isEnabled( 'global-styles/on-personal-plan' ) ) { + if ( isGlobalStylesOnPersonal ) { minimumPlan = tierMinimumUpsellPlan; } else if ( tierMinimumUpsellPlan === PLAN_PERSONAL && isLockedStyleVariation ) { minimumPlan = PLAN_PREMIUM; @@ -524,4 +524,9 @@ const connectOptionsHoc = connect( } ); -export const connectOptions = compose( localize, withIsFSEActive, connectOptionsHoc ); +export const connectOptions = compose( + localize, + withIsFSEActive, + withSiteGlobalStylesOnPersonal, + connectOptionsHoc +); diff --git a/client/sites/settings/site/privacy/notice.tsx b/client/sites/settings/site/privacy/notice.tsx index 059caa51ce4a6..8a08a9de12d60 100644 --- a/client/sites/settings/site/privacy/notice.tsx +++ b/client/sites/settings/site/privacy/notice.tsx @@ -1,5 +1,4 @@ import { recordTracksEvent } from '@automattic/calypso-analytics'; -import { isEnabled } from '@automattic/calypso-config'; import { FEATURE_STYLE_CUSTOMIZATION, PLAN_PREMIUM, @@ -8,6 +7,7 @@ import { } from '@automattic/calypso-products'; import { Button, Gridicon } from '@automattic/components'; import { useTranslate } from 'i18n-calypso'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; import type { SiteDetails } from '@automattic/data-stores'; interface SiteSettingPrivacyNoticeProps { @@ -18,7 +18,7 @@ interface SiteSettingPrivacyNoticeProps { const SiteSettingPrivacyNotice = ( { selectedSite, siteSlug }: SiteSettingPrivacyNoticeProps ) => { const translate = useTranslate(); // @TODO Cleanup once the test phase is over. - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) + const upgradeToPlan = useSiteGlobalStylesOnPersonal( selectedSite?.ID ) ? PLAN_PERSONAL : PLAN_PREMIUM; const upgradeUrl = `/plans/${ siteSlug }?plan=${ upgradeToPlan }&feature=${ FEATURE_STYLE_CUSTOMIZATION }`; diff --git a/client/state/sites/hooks/use-site-global-styles-on-personal.ts b/client/state/sites/hooks/use-site-global-styles-on-personal.ts new file mode 100644 index 0000000000000..8b5c1ab606ca3 --- /dev/null +++ b/client/state/sites/hooks/use-site-global-styles-on-personal.ts @@ -0,0 +1,21 @@ +import { isEnabled } from '@automattic/calypso-config'; +import { useEffect } from 'react'; +import { useSiteGlobalStylesStatus } from 'calypso/state/sites/hooks/use-site-global-styles-status'; + +type SiteIdOrSlug = number | string | null; + +export function useSiteGlobalStylesOnPersonal( siteIdOrSlug: SiteIdOrSlug = null ): boolean { + const { globalStylesInPersonalPlan } = useSiteGlobalStylesStatus( siteIdOrSlug ); + + // Return true if global styles are enabled in the Personal Plan through feature flag or experiment. + const isGlobalStylesOnPersonalEnabled = + globalStylesInPersonalPlan || isEnabled( 'global-styles/on-personal-plan' ); + + useEffect( () => { + if ( typeof window !== 'undefined' ) { + ( window as any ).isGlobalStylesOnPersonal = isGlobalStylesOnPersonalEnabled; + } + }, [ isGlobalStylesOnPersonalEnabled ] ); + + return isGlobalStylesOnPersonalEnabled; +} diff --git a/client/state/sites/hooks/use-site-global-styles-status.ts b/client/state/sites/hooks/use-site-global-styles-status.ts index 20d1922f2c287..ddb0096f21108 100644 --- a/client/state/sites/hooks/use-site-global-styles-status.ts +++ b/client/state/sites/hooks/use-site-global-styles-status.ts @@ -1,12 +1,15 @@ +import { ExperimentAssignment } from '@automattic/explat-client'; import { useQuery } from '@tanstack/react-query'; import wpcom from 'calypso/lib/wp'; import { useSelector } from 'calypso/state'; +import { isUserLoggedIn } from 'calypso/state/current-user/selectors'; import { getSite } from 'calypso/state/sites/selectors'; import { getSelectedSiteId } from 'calypso/state/ui/selectors'; export type GlobalStylesStatus = { shouldLimitGlobalStyles: boolean; globalStylesInUse: boolean; + globalStylesInPersonalPlan?: boolean; }; // While we are loading the Global Styles Info we can't assume that we should limit global styles, or we would be @@ -14,16 +17,74 @@ export type GlobalStylesStatus = { const DEFAULT_GLOBAL_STYLES_INFO: GlobalStylesStatus = { shouldLimitGlobalStyles: false, globalStylesInUse: false, + globalStylesInPersonalPlan: false, }; -const getGlobalStylesInfoForSite = ( siteId: number | null ): Promise< GlobalStylesStatus > => { - if ( siteId === null ) { +/* + * We cannot import `loadExperimentAssignment` directly from 'calypso/lib/explat' + * because it runs a side effect that produces an error on SSR contexts. + */ +let loadExperimentAssignment = ( experimentName: string ): Promise< ExperimentAssignment > => + Promise.resolve( { experimentName, variationName: null, retrievedTimestamp: 0, ttl: 0 } ); + +if ( typeof window !== 'undefined' ) { + import( 'calypso/lib/explat' ) + .then( ( module ) => { + loadExperimentAssignment = module.loadExperimentAssignment; + } ) + // eslint-disable-next-line @typescript-eslint/no-empty-function + .catch( () => {} ); +} + +function shouldRunGlobalStylesOnPersonalExperiment( + siteId: number | null, + userLoggedIn: boolean +): boolean { + // Do not run it on SSR contexts. + if ( typeof window === 'undefined' ) { + return false; + } + + // Always run it if a site has been selected. + if ( siteId !== null ) { + return true; + } + + // Do not run it on the logged-out theme showcase. + if ( ! userLoggedIn && window.location.pathname.startsWith( '/theme' ) ) { + return false; + } + + // Run it by default. Ideally, we should not run it if the user is logged out, but + // we cannot rely on the `isUserLoggedIn` selector for users who just signed up + // (see pbxNRc-2HR-p2#comment-4607). So, we assume that this hook is not used in + // any logged-out context apart from the theme showcase. + return true; +} + +const getGlobalStylesInfoForSite = ( + siteId: number | null, + userLoggedIn: boolean = false +): Promise< GlobalStylesStatus > => { + if ( ! shouldRunGlobalStylesOnPersonalExperiment( siteId, userLoggedIn ) ) { return Promise.resolve( { shouldLimitGlobalStyles: true, globalStylesInUse: false, + globalStylesInPersonalPlan: false, } ); } + if ( siteId === null ) { + return loadExperimentAssignment( 'calypso_plans_global_styles_personal_20240127' ).then( + ( experimentAssignment ) => + Promise.resolve( { + shouldLimitGlobalStyles: true, + globalStylesInUse: false, + globalStylesInPersonalPlan: experimentAssignment.variationName === 'treatment', + } ) + ); + } + return wpcom.req .get( { path: `sites/${ siteId }/global-styles/status`, @@ -40,6 +101,7 @@ export function useSiteGlobalStylesStatus( siteIdOrSlug: number | string | null = null ): GlobalStylesStatus { const selectedSiteId = useSelector( getSelectedSiteId ); + const userLoggedIn = useSelector( isUserLoggedIn ); // When site id is null it means that the site hasn't been created yet. const siteId = useSelector( ( state ) => { @@ -54,7 +116,7 @@ export function useSiteGlobalStylesStatus( const { data: globalStylesInfo } = useQuery( { queryKey: [ 'globalStylesInfo', siteId ], - queryFn: () => getGlobalStylesInfoForSite( siteId ), + queryFn: () => getGlobalStylesInfoForSite( siteId, userLoggedIn ), placeholderData: DEFAULT_GLOBAL_STYLES_INFO, refetchOnWindowFocus: false, } ); diff --git a/client/state/sites/hooks/with-site-global-styles-on-personal.tsx b/client/state/sites/hooks/with-site-global-styles-on-personal.tsx new file mode 100644 index 0000000000000..69a40bd24cba2 --- /dev/null +++ b/client/state/sites/hooks/with-site-global-styles-on-personal.tsx @@ -0,0 +1,11 @@ +import { createHigherOrderComponent } from '@wordpress/compose'; +import { useSiteGlobalStylesOnPersonal } from 'calypso/state/sites/hooks/use-site-global-styles-on-personal'; + +export const withSiteGlobalStylesOnPersonal = createHigherOrderComponent( + ( Wrapped ) => ( props ) => { + const isGlobalStylesOnPersonal = useSiteGlobalStylesOnPersonal(); + + return ; + }, + 'withSiteGlobalStylesOnPersonal' +); diff --git a/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts b/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts new file mode 100644 index 0000000000000..6443d064ce91c --- /dev/null +++ b/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts @@ -0,0 +1,11 @@ +declare global { + interface Window { + isGlobalStylesOnPersonal?: boolean; + } +} +export function isGlobalStylesOnPersonalEnabled(): boolean { + if ( typeof window === 'undefined' ) { + return false; + } + return !! window.isGlobalStylesOnPersonal; +} diff --git a/packages/calypso-products/src/plans-list.tsx b/packages/calypso-products/src/plans-list.tsx index 41251480e1dab..f5863b3037505 100644 --- a/packages/calypso-products/src/plans-list.tsx +++ b/packages/calypso-products/src/plans-list.tsx @@ -438,8 +438,10 @@ import { FEATURE_THEMES_PREMIUM_AND_STORE, FEATURE_UNLIMITED_ENTITIES, JETPACK_TAG_FOR_BLOGGERS, + FEATURE_CONNECT_ANALYTICS, + FEATURE_JETPACK_SOCIAL_V1_MONTHLY, } from './constants'; -import { FEATURE_CONNECT_ANALYTICS, FEATURE_JETPACK_SOCIAL_V1_MONTHLY } from './constants/features'; +import { isGlobalStylesOnPersonalEnabled } from './is-global-styles-on-personal-enabled'; import { getPlanBusinessTitle, getPlanEcommerceTitle, @@ -747,7 +749,7 @@ const getPlanPersonalDetails = (): IncompleteWPcomPlan => ( { FEATURE_JETPACK_ESSENTIAL, FEATURE_FAST_SUPPORT_FROM_EXPERTS, FEATURE_FREE_THEMES, - isEnabled( 'global-styles/on-personal-plan' ) ? FEATURE_STYLE_CUSTOMIZATION : null, + isGlobalStylesOnPersonalEnabled() ? FEATURE_STYLE_CUSTOMIZATION : null, FEATURE_6GB_STORAGE, FEATURE_NO_ADS, FEATURE_MEMBERSHIPS, @@ -761,7 +763,7 @@ const getPlanPersonalDetails = (): IncompleteWPcomPlan => ( { FEATURE_FREE_THEMES, ]; - return isEnabled( 'global-styles/on-personal-plan' ) + return isGlobalStylesOnPersonalEnabled() ? [ ...baseFeatures, FEATURE_STYLE_CUSTOMIZATION ] : baseFeatures; }, @@ -784,7 +786,7 @@ const getPlanPersonalDetails = (): IncompleteWPcomPlan => ( { FEATURE_FAST_SUPPORT_FROM_EXPERTS, ]; - return isEnabled( 'global-styles/on-personal-plan' ) + return isGlobalStylesOnPersonalEnabled() ? [ ...baseFeatures, FEATURE_STYLE_CUSTOMIZATION ] : baseFeatures; }, @@ -797,7 +799,7 @@ const getPlanPersonalDetails = (): IncompleteWPcomPlan => ( { FEATURE_FAST_SUPPORT_FROM_EXPERTS, ]; - return isEnabled( 'global-styles/on-personal-plan' ) + return isGlobalStylesOnPersonalEnabled() ? [ ...baseFeatures, FEATURE_STYLE_CUSTOMIZATION ] : baseFeatures; }, @@ -813,7 +815,7 @@ const getPlanPersonalDetails = (): IncompleteWPcomPlan => ( { FEATURE_PREMIUM_THEMES, ]; - return isEnabled( 'global-styles/on-personal-plan' ) + return isGlobalStylesOnPersonalEnabled() ? [ ...baseFeatures, FEATURE_STYLE_CUSTOMIZATION ] : baseFeatures; }, diff --git a/packages/global-styles/src/components/global-styles-variations/index.tsx b/packages/global-styles/src/components/global-styles-variations/index.tsx index 6bf3edbc5fb32..773970a853dca 100644 --- a/packages/global-styles/src/components/global-styles-variations/index.tsx +++ b/packages/global-styles/src/components/global-styles-variations/index.tsx @@ -1,4 +1,3 @@ -import { isEnabled } from '@automattic/calypso-config'; import { PLAN_PREMIUM, getPlan, PLAN_PERSONAL } from '@automattic/calypso-products'; import { PremiumBadge } from '@automattic/components'; import { useHasEnTranslation } from '@automattic/i18n-utils'; @@ -19,6 +18,12 @@ import GlobalStylesVariationPreview from './preview'; import type { GlobalStylesObject } from '../../types'; import './style.scss'; +declare global { + interface Window { + isGlobalStylesOnPersonal?: boolean; + } +} + interface GlobalStylesVariationProps { globalStylesVariation: GlobalStylesObject; isActive: boolean; @@ -33,6 +38,7 @@ interface GlobalStylesVariationsProps { showOnlyHoverViewDefaultVariation?: boolean; splitDefaultVariation?: boolean; needsUpgrade?: boolean; + isGlobalStylesOnPersonal?: boolean; onSelect: ( globalStylesVariation: GlobalStylesObject ) => void; } @@ -100,13 +106,12 @@ const GlobalStylesVariations = ( { showOnlyHoverViewDefaultVariation, splitDefaultVariation = true, needsUpgrade = true, + isGlobalStylesOnPersonal = window.isGlobalStylesOnPersonal ?? false, onSelect, }: GlobalStylesVariationsProps ) => { const hasEnTranslation = useHasEnTranslation(); const isRegisteredCoreBlocks = useRegisterCoreBlocks(); - const upgradeToPlan = isEnabled( 'global-styles/on-personal-plan' ) - ? PLAN_PERSONAL - : PLAN_PREMIUM; + const upgradeToPlan = isGlobalStylesOnPersonal ? PLAN_PERSONAL : PLAN_PREMIUM; const variationDescription = needsUpgrade ? translate(