From 6dbbeda45215d19d6414762aa09fc2b4b26dceb5 Mon Sep 17 00:00:00 2001 From: Leonidas Milosis Date: Mon, 4 Nov 2024 15:12:35 +0200 Subject: [PATCH 1/7] Stop showing Local notice about missing org info if it is filled in the FTC --- packages/js/src/general/app.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/js/src/general/app.js b/packages/js/src/general/app.js index 545e162b790..68958c5b629 100644 --- a/packages/js/src/general/app.js +++ b/packages/js/src/general/app.js @@ -131,6 +131,11 @@ const App = () => { if ( notice.id === "yoast-first-time-configuration-notice" && wpseoFirstTimeConfigurationData.finishedSteps.includes( FTC_STEPS.personalPreferences ) ) { return null; } + /* If all the site representation info in First-time configuration is added, + we remove the relevant notice from Local. */ + if ( notice.id === "yoast-local-missing-organization-info-notice" && ( wpseoFirstTimeConfigurationData.companyLogo && wpseoFirstTimeConfigurationData.companyLogoId && wpseoFirstTimeConfigurationData.companyName ) ) { + return null; + } return ( Date: Tue, 5 Nov 2024 11:40:33 +0200 Subject: [PATCH 2/7] Add resolved notices to the store and hide notices based on that as well --- .../first-time-configuration-steps.js | 7 ++++ packages/js/src/general/app.js | 18 ++++---- packages/js/src/general/initialize.js | 2 + .../general/store/first-time-configuration.js | 42 +++++++++++++++++++ packages/js/src/general/store/index.js | 7 +++- 5 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 packages/js/src/general/store/first-time-configuration.js diff --git a/packages/js/src/first-time-configuration/first-time-configuration-steps.js b/packages/js/src/first-time-configuration/first-time-configuration-steps.js index 06facc620c9..84bd332efdb 100644 --- a/packages/js/src/first-time-configuration/first-time-configuration-steps.js +++ b/packages/js/src/first-time-configuration/first-time-configuration-steps.js @@ -160,6 +160,7 @@ function calculateInitialState( windowObject, isStepFinished ) { */ export default function FirstTimeConfigurationSteps() { const { removeAlert } = useDispatch( STORE_NAME ); + const { resolveNotice } = useDispatch( STORE_NAME ); const [ finishedSteps, setFinishedSteps ] = useState( window.wpseoFirstTimeConfigurationData.finishedSteps ); const isStepFinished = useCallback( ( stepId ) => { @@ -203,6 +204,12 @@ export default function FirstTimeConfigurationSteps() { const isStep3Finished = isStepFinished( STEPS.socialProfiles ); const isStep4Finished = isStepFinished( STEPS.personalPreferences ); + useEffect( () => { + if ( isStep4Finished ) { + resolveNotice( "yoast-first-time-configuration-notice" ); + } + }, [ finishedSteps ] ); + const setTracking = useCallback( ( value ) => { dispatch( { type: "SET_TRACKING", payload: parseInt( value, 10 ) } ); } ); diff --git a/packages/js/src/general/app.js b/packages/js/src/general/app.js index 68958c5b629..6ad16f8a2e9 100644 --- a/packages/js/src/general/app.js +++ b/packages/js/src/general/app.js @@ -5,7 +5,7 @@ import { Transition } from "@headlessui/react"; import { AdjustmentsIcon, BellIcon } from "@heroicons/react/outline"; import { __ } from "@wordpress/i18n"; import { useCallback, useEffect, useMemo } from "@wordpress/element"; -import { select, useDispatch } from "@wordpress/data"; +import { select, useDispatch, useSelect } from "@wordpress/data"; import { addQueryArgs } from "@wordpress/url"; import { Notifications, SidebarNavigation, useSvgAria } from "@yoast/ui-library"; import PropTypes from "prop-types"; @@ -18,7 +18,6 @@ import Notice from "./components/notice"; import WebinarPromoNotification from "../components/WebinarPromoNotification"; import { shouldShowWebinarPromotionNotificationInDashboard } from "../helpers/shouldShowWebinarPromotionNotification"; import { useNotificationCountSync } from "./hooks/use-notification-count-sync"; -import { STEPS as FTC_STEPS } from "../first-time-configuration/constants"; /** * @param {string} [idSuffix] Extra id suffix. Can prevent double IDs on the page. @@ -72,6 +71,7 @@ Menu.propTypes = { */ const App = () => { const notices = useMemo( getMigratingNoticeInfo, [] ); + const resolvedNotices = useSelect( ( select ) => select( STORE_NAME ).selectResolvedNotices(), [] ); useEffect( () => { deleteMigratingNotices( notices ); @@ -126,20 +126,16 @@ const App = () => { } { notices.length > 0 &&
{ notices.map( ( notice, index ) => { - /* If the last step of the First-time configuration has been completed, - we remove the First-time configuration notice. */ - if ( notice.id === "yoast-first-time-configuration-notice" && wpseoFirstTimeConfigurationData.finishedSteps.includes( FTC_STEPS.personalPreferences ) ) { - return null; - } - /* If all the site representation info in First-time configuration is added, - we remove the relevant notice from Local. */ - if ( notice.id === "yoast-local-missing-organization-info-notice" && ( wpseoFirstTimeConfigurationData.companyLogo && wpseoFirstTimeConfigurationData.companyLogoId && wpseoFirstTimeConfigurationData.companyName ) ) { + const noticeID = notice.id || "yoast-general-page-notice-" + index; + + if ( resolvedNotices.includes( noticeID ) ) { return null; } + return ( diff --git a/packages/js/src/general/initialize.js b/packages/js/src/general/initialize.js index c2541e64d69..0f911c62238 100644 --- a/packages/js/src/general/initialize.js +++ b/packages/js/src/general/initialize.js @@ -6,6 +6,7 @@ import { Root } from "@yoast/ui-library"; import { get } from "lodash"; import { LINK_PARAMS_NAME } from "../shared-admin/store"; import { ALERT_CENTER_NAME } from "./store/alert-center"; +import { FTC_NAME } from "./store/first-time-configuration"; import { createHashRouter, createRoutesFromElements, Route, RouterProvider } from "react-router-dom"; import App from "./app"; import { STORE_NAME } from "./constants"; @@ -24,6 +25,7 @@ domReady( () => { currentPromotions: { promotions: get( window, "wpseoScriptData.currentPromotions", [] ) }, dismissedAlerts: get( window, "wpseoScriptData.dismissedAlerts", {} ), isPremium: get( window, "wpseoScriptData.preferences.isPremium", false ), + [ FTC_NAME ]: { resolvedNotices: [] }, }, } ); const isRtl = select( STORE_NAME ).selectPreference( "isRtl", false ); diff --git a/packages/js/src/general/store/first-time-configuration.js b/packages/js/src/general/store/first-time-configuration.js new file mode 100644 index 00000000000..05a912e71f6 --- /dev/null +++ b/packages/js/src/general/store/first-time-configuration.js @@ -0,0 +1,42 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { get } from "lodash"; + +export const FTC_NAME = "firstTimeConfiguration"; + +const slice = createSlice( { + name: FTC_NAME, + initialState: { resolvedNotices: [] }, + reducers: { + /** + * @param {Object} state The state of the slice. + * @param {string} noticeID The ID of the notice to resolve. + * @returns {void} + */ + resolveNotice( state, { payload: noticeID } ) { + if ( ! state.resolvedNotices.includes( noticeID ) ) { + state.resolvedNotices.push( noticeID ); + } + }, + /** + * @param {Object} state The state of the slice. + * @param {string} noticeID The ID of the notice to unresolve. + * @returns {void} + */ + unresolveNotice( state, { payload: noticeID } ) { + state.resolvedNotices = state.resolvedNotices.filter( ( id ) => id !== noticeID ); + }, + }, +} ); + +/** + * @returns {Object} The initial state. + */ +export const getInitialFirstTimeConfigurationState = slice.getInitialState; + +export const firstTimeConfigurationSelectors = { + selectResolvedNotices: state => get( state, `${ FTC_NAME }.resolvedNotices`, [] ), +}; + +export const firstTimeConfigurationActions = slice.actions; + +export const firstTimeConfigurationReducer = slice.reducer; diff --git a/packages/js/src/general/store/index.js b/packages/js/src/general/store/index.js index 71369cd3224..25c7deb5cd9 100644 --- a/packages/js/src/general/store/index.js +++ b/packages/js/src/general/store/index.js @@ -7,8 +7,9 @@ import preferences, { createInitialPreferencesState, preferencesActions, prefere import { reducers, selectors, actions } from "@yoast/externals/redux"; import * as dismissedAlertsControls from "../../redux/controls/dismissedAlerts"; import { alertCenterReducer, alertCenterActions, alertCenterSelectors, getInitialAlertCenterState, alertCenterControls, ALERT_CENTER_NAME } from "./alert-center"; +import { firstTimeConfigurationActions, firstTimeConfigurationReducer, firstTimeConfigurationSelectors, FTC_NAME, getInitialFirstTimeConfigurationState } from "./first-time-configuration"; -const { currentPromotions, dismissedAlerts, isPremium } = reducers; +const { currentPromotions, dismissedAlerts, isPremium } = reducers; const { isAlertDismissed, getIsPremium, isPromotionActive } = selectors; const { dismissAlert, setCurrentPromotions, setDismissedAlerts, setIsPremium } = actions; @@ -28,6 +29,7 @@ const createStore = ( { initialState } ) => { setCurrentPromotions, setDismissedAlerts, setIsPremium, + ...firstTimeConfigurationActions, }, selectors: { ...linkParamsSelectors, @@ -36,6 +38,7 @@ const createStore = ( { initialState } ) => { isAlertDismissed, getIsPremium, isPromotionActive, + ...firstTimeConfigurationSelectors, }, initialState: merge( {}, @@ -44,6 +47,7 @@ const createStore = ( { initialState } ) => { preferences: createInitialPreferencesState(), [ ALERT_CENTER_NAME ]: getInitialAlertCenterState(), currentPromotions: { promotions: [] }, + [ FTC_NAME ]: getInitialFirstTimeConfigurationState(), }, initialState ), @@ -54,6 +58,7 @@ const createStore = ( { initialState } ) => { currentPromotions, dismissedAlerts, isPremium, + [ FTC_NAME ]: firstTimeConfigurationReducer, } ), controls: { ...alertCenterControls, From 241711958bcb0c82c8c5d0d0d54653e5f5a07710 Mon Sep 17 00:00:00 2001 From: Leonidas Milosis Date: Tue, 5 Nov 2024 12:29:00 +0200 Subject: [PATCH 3/7] Fix linting --- packages/js/src/general/app.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/js/src/general/app.js b/packages/js/src/general/app.js index 6ad16f8a2e9..2383b43b590 100644 --- a/packages/js/src/general/app.js +++ b/packages/js/src/general/app.js @@ -1,11 +1,10 @@ /* eslint-disable complexity */ -/* global wpseoFirstTimeConfigurationData */ import { Transition } from "@headlessui/react"; import { AdjustmentsIcon, BellIcon } from "@heroicons/react/outline"; import { __ } from "@wordpress/i18n"; import { useCallback, useEffect, useMemo } from "@wordpress/element"; -import { select, useDispatch, useSelect } from "@wordpress/data"; +import { select, useDispatch } from "@wordpress/data"; import { addQueryArgs } from "@wordpress/url"; import { Notifications, SidebarNavigation, useSvgAria } from "@yoast/ui-library"; import PropTypes from "prop-types"; @@ -71,7 +70,7 @@ Menu.propTypes = { */ const App = () => { const notices = useMemo( getMigratingNoticeInfo, [] ); - const resolvedNotices = useSelect( ( select ) => select( STORE_NAME ).selectResolvedNotices(), [] ); + const resolvedNotices = select( STORE_NAME ).selectResolvedNotices(); useEffect( () => { deleteMigratingNotices( notices ); From 11ba420f3a753bad9e1a647e1874df057e72c5e0 Mon Sep 17 00:00:00 2001 From: Leonidas Milosis Date: Tue, 5 Nov 2024 15:03:02 +0200 Subject: [PATCH 4/7] Unresolve notice when org info is missing --- .../first-time-configuration-steps.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/js/src/first-time-configuration/first-time-configuration-steps.js b/packages/js/src/first-time-configuration/first-time-configuration-steps.js index 84bd332efdb..a61faef0c40 100644 --- a/packages/js/src/first-time-configuration/first-time-configuration-steps.js +++ b/packages/js/src/first-time-configuration/first-time-configuration-steps.js @@ -159,8 +159,7 @@ function calculateInitialState( windowObject, isStepFinished ) { * @returns {WPElement} The FirstTimeConfigurationSteps component. */ export default function FirstTimeConfigurationSteps() { - const { removeAlert } = useDispatch( STORE_NAME ); - const { resolveNotice } = useDispatch( STORE_NAME ); + const { removeAlert, resolveNotice, unresolveNotice } = useDispatch( STORE_NAME ); const [ finishedSteps, setFinishedSteps ] = useState( window.wpseoFirstTimeConfigurationData.finishedSteps ); const isStepFinished = useCallback( ( stepId ) => { @@ -205,6 +204,12 @@ export default function FirstTimeConfigurationSteps() { const isStep4Finished = isStepFinished( STEPS.personalPreferences ); useEffect( () => { + if ( state.companyLogo !== "" && state.companyLogoId !== 0 && state.companyName !== "" ) { + resolveNotice( "yoast-local-missing-organization-info-notice" ); + } else { + unresolveNotice( "yoast-local-missing-organization-info-notice" ); + } + if ( isStep4Finished ) { resolveNotice( "yoast-first-time-configuration-notice" ); } From c505c69ce73f9ba2937c478611e629c69e759e6b Mon Sep 17 00:00:00 2001 From: Leonidas Milosis Date: Tue, 5 Nov 2024 16:30:30 +0200 Subject: [PATCH 5/7] Resolve and unresolve notices only when the relevant steps are being saved --- .../first-time-configuration-steps.js | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/js/src/first-time-configuration/first-time-configuration-steps.js b/packages/js/src/first-time-configuration/first-time-configuration-steps.js index a61faef0c40..038a8507533 100644 --- a/packages/js/src/first-time-configuration/first-time-configuration-steps.js +++ b/packages/js/src/first-time-configuration/first-time-configuration-steps.js @@ -203,18 +203,6 @@ export default function FirstTimeConfigurationSteps() { const isStep3Finished = isStepFinished( STEPS.socialProfiles ); const isStep4Finished = isStepFinished( STEPS.personalPreferences ); - useEffect( () => { - if ( state.companyLogo !== "" && state.companyLogoId !== 0 && state.companyName !== "" ) { - resolveNotice( "yoast-local-missing-organization-info-notice" ); - } else { - unresolveNotice( "yoast-local-missing-organization-info-notice" ); - } - - if ( isStep4Finished ) { - resolveNotice( "yoast-first-time-configuration-notice" ); - } - }, [ finishedSteps ] ); - const setTracking = useCallback( ( value ) => { dispatch( { type: "SET_TRACKING", payload: parseInt( value, 10 ) } ); } ); @@ -249,6 +237,13 @@ export default function FirstTimeConfigurationSteps() { removeStepError( STEPS.siteRepresentation ); finishSteps( STEPS.siteRepresentation ); window.wpseoFirstTimeConfigurationData = { ...window.wpseoFirstTimeConfigurationData, ...state }; + + if ( state.companyLogo !== "" && state.companyLogoId !== 0 && state.companyName !== "" ) { + resolveNotice( "yoast-local-missing-organization-info-notice" ); + } else { + unresolveNotice( "yoast-local-missing-organization-info-notice" ); + } + return true; } ) .catch( ( e ) => { @@ -315,6 +310,9 @@ export default function FirstTimeConfigurationSteps() { .then( () => { removeStepError( STEPS.personalPreferences ); window.wpseoFirstTimeConfigurationData.tracking = state.tracking; + + resolveNotice( "yoast-first-time-configuration-notice" ); + return true; } ) .catch( e => { From 02de0584d1ad7d784a74ce2986de50a90db0674e Mon Sep 17 00:00:00 2001 From: Leonidas Milosis Date: Wed, 6 Nov 2024 09:54:45 +0200 Subject: [PATCH 6/7] Cache resolving functions between renders --- .../first-time-configuration-steps.js | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/js/src/first-time-configuration/first-time-configuration-steps.js b/packages/js/src/first-time-configuration/first-time-configuration-steps.js index 038a8507533..7bad742714c 100644 --- a/packages/js/src/first-time-configuration/first-time-configuration-steps.js +++ b/packages/js/src/first-time-configuration/first-time-configuration-steps.js @@ -211,6 +211,18 @@ export default function FirstTimeConfigurationSteps() { dispatch( { type: "SET_ERROR_FIELDS", payload: value } ); } ); + const resolveLocalNotice = useCallback( () => { + if ( state.companyLogo !== "" && state.companyLogoId !== 0 && state.companyName !== "" ) { + resolveNotice( "yoast-local-missing-organization-info-notice" ); + } else { + unresolveNotice( "yoast-local-missing-organization-info-notice" ); + } + }, [ resolveNotice, unresolveNotice, state.companyLogo, state.companyLogoId, state.companyName ] ); + + const resolveFTCNotice = useCallback( () => { + resolveNotice( "yoast-first-time-configuration-notice" ); + }, [ resolveNotice ] ); + const isCompanyAndEmpty = state.companyOrPerson === "company" && ( ! state.companyName || ( ! state.companyLogo && ! state.companyLogoFallback ) || ! state.websiteName ); const isPersonAndEmpty = state.companyOrPerson === "person" && ( ! state.personId || ( ! state.personLogo && ! state.personLogoFallback ) || ! state.websiteName ); @@ -238,11 +250,7 @@ export default function FirstTimeConfigurationSteps() { finishSteps( STEPS.siteRepresentation ); window.wpseoFirstTimeConfigurationData = { ...window.wpseoFirstTimeConfigurationData, ...state }; - if ( state.companyLogo !== "" && state.companyLogoId !== 0 && state.companyName !== "" ) { - resolveNotice( "yoast-local-missing-organization-info-notice" ); - } else { - unresolveNotice( "yoast-local-missing-organization-info-notice" ); - } + resolveLocalNotice(); return true; } ) @@ -311,7 +319,7 @@ export default function FirstTimeConfigurationSteps() { removeStepError( STEPS.personalPreferences ); window.wpseoFirstTimeConfigurationData.tracking = state.tracking; - resolveNotice( "yoast-first-time-configuration-notice" ); + resolveFTCNotice(); return true; } ) From 5c1f9e7a7960b278486a47e1a22eed350774c669 Mon Sep 17 00:00:00 2001 From: Leonidas Milosis Date: Wed, 6 Nov 2024 16:31:24 +0200 Subject: [PATCH 7/7] Use useSelect instead of select --- packages/js/src/general/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/js/src/general/app.js b/packages/js/src/general/app.js index bb7901144d5..69ae0efea4f 100644 --- a/packages/js/src/general/app.js +++ b/packages/js/src/general/app.js @@ -2,7 +2,7 @@ import { Transition } from "@headlessui/react"; import { AdjustmentsIcon, BellIcon } from "@heroicons/react/outline"; -import { select, useDispatch } from "@wordpress/data"; +import { useDispatch, useSelect } from "@wordpress/data"; import { useCallback, useEffect, useMemo } from "@wordpress/element"; import { __ } from "@wordpress/i18n"; import { addQueryArgs } from "@wordpress/url"; @@ -69,7 +69,7 @@ Menu.propTypes = { */ const App = () => { const notices = useMemo( getMigratingNoticeInfo, [] ); - const resolvedNotices = select( STORE_NAME ).selectResolvedNotices(); + const resolvedNotices = useSelect( select => select( STORE_NAME ).selectResolvedNotices(), [] ); useEffect( () => { deleteMigratingNotices( notices ); @@ -84,7 +84,7 @@ const App = () => { setAlertToggleError( null ); }, [ setAlertToggleError ] ); - const linkParams = select( STORE_NAME ).selectLinkParams(); + const linkParams = useSelect( select => select( STORE_NAME ).selectLinkParams(), [] ); const webinarIntroSettingsUrl = addQueryArgs( "https://yoa.st/webinar-intro-settings", linkParams ); return (