Make timeout for `view_dashboard` surveys more reliable and extend it
See original GitHub issueIt is currently possible to see a few too many surveys in short time frame, which isn’t that great from a user experience perspective. This is particularly related to the view_dashboard trigger, which several surveys get attached to. While there is already a 1 hour timeout, it is only stored on the client-side and it is regularly invalidated e.g. when activating a new module, so that can result in an overload of surveys being shown in little time.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
- The implementation of the timeout mechanism for remote-controlled surveys should be modified to rely on a server-side storage (i.e. a storage which persists beyond user sessions in the browser). In practice, this only affects the
view_dashboardtrigger, which is the only survey trigger ID currently relying on a timeout, but the implementation should be generic enough to support other triggers in the future.- The storage should rely on user meta, so that this is stored per user.
- The storage should support an undefined number of trigger IDs, so it shouldn’t be a single meta key just for the
view_dashboardtrigger. - A REST API endpoint to set a trigger timeout should be added, which allows specifying the trigger ID and requested timeout.
- The client has to then receive awareness of which trigger IDs are currently “on timeout” per user, replacing the session storage lookup for this (potentially with a preloaded REST API endpoint to get the list of trigger timeouts).
- The timeout for the
view_dashboardtrigger specifically should be extended to 24 hours (instead of 1 hour as it is now).
Implementation Brief
Create new REST endpoints
Note: The Dismissed Items endpoints and related code under includes/Core/Dismissals can serve as a reference and broadly be copied here.
Working in the directory includes/Core/User_Surveys:
- Create a new class called
Survey_Timeoutsto represent the survey timeouts as a user setting.- This can mostly be a copy of
includes/Core/Dismissals/Dismissed_Items.php, renamed for survey timeouts. OPTIONshould begooglesitekit_survey_timeouts.get_dismissed_itemsandfilter_dismissed_itemsshould be renamedget_survey_timeoutsandfilter_survey_timeouts.- There is no equivalent need for the method
is_dismissedand this can be ignored/removed inSurvey_Timeouts.
- This can mostly be a copy of
- Create a new class
User_Surveys, as a container forSurvey_TimeoutsandREST_User_Surveys_Controller, following the same pattern asincludes/Core/Dismissals/Dismissals.php.- Update
includes/Plugin.phpto create and registerUser_Surveysinstead ofREST_User_Surveys_Controller.
- Update
- Add new REST routes to
REST_User_Surveys_Controller:GET core/user/data/survey-timeoutsfor retrieving the list of survey timeouts.POST core/user/data/survey-timeoutfor setting a survey timeout.- These can be copied from the
GET core/user/data/dismissed-itemsandPOST core/user/data/dismiss-itemroutes inincludes/Core/Dismissals/REST_Dismissals_Controller.php. - Other than renaming any “dismiss item” references to “survey timeout”, these can be directly copied.
- Ensure
core/user/data/survey-timeoutsis preloaded by adding it to thegooglesitekit_apifetch_preload_pathsfilter in theregistermethod.- See the corresponding use of
googlesitekit_apifetch_preload_pathsinREST_Dismissals_Controllerfor an example.
- See the corresponding use of
Create Redux interface to new endpoints
Note: The Redux store for Dismissed Items in assets/js/googlesitekit/datastore/user/dismissed-items.js can serve as a reference and broadly be copied here.
- Create a new file
assets/js/googlesitekit/datastore/user/survey-timeouts.js.- This can essentially be a copy of
assets/js/googlesitekit/datastore/user/dismissed-items.js, renamed for survey timeouts. - The
createFetchStorestore for theGET .../survey-timeoutsendpoint should have thebaseNameofgetSurveyTimeouts. - The
createFetchStorestore for thePOST .../survey-timeoutendpoint should have thebaseNameofsetSurveyTimeout. dismissItemshould be renamedsetSurveyTimeout.getDismissedItemsshould be renamedgetSurveyTimeouts.isItemDismissedshould be renamedisSurveyTimedOut.isDismissingItemshould be renamedisTimingOutSurvey.- All other names should be updated to appropriate survey timeout related names.
- This can essentially be a copy of
- Combine the new store in
assets/js/googlesitekit/datastore/user/index.js.
Update triggerSurvey action to use backend data
Update the triggerSurvey action in assets/js/googlesitekit/datastore/user/surveys.js to use backend data via the new store actions and selectors, instead of checking local storage.
- Replace the condition that checks for a cache hit with a condition that checks:
- If the
isSurveyTimedOutselector returnsfalseand theisTimingOutSurveyselector returnsfalse, for the giventriggerID. - Note,
isSurveyTimedOutmust be checked for explicit=== false, asundefinedindicates the state is not resolved yet.
- If the
- Within the
setTimeoutcallback:- Replace the call to
setItemwith adispatchof thesetSurveyTimeoutaction, passing in thetriggerIDandttl. - Await the above action. If successful, the response will be the updated set of survey timeouts.
- With a successful response, dispatch the
receiveGetSurveyTimeoutsaction with the received survey timeouts, thus updating the local state. - If the response has an error, it can be ignored.
- Replace the call to
Replace useMount with useEffect in SurveyViewTrigger
Within SurveyViewTrigger:
- Create a variable,
shouldTriggerSurvey, containing the boolean value of the condition as described above:- The condition is
trueif theisSurveyTimedOutselector returnsfalseand theisTimingOutSurveyselector returnsfalse, for the giventriggerID. - Otherwise, it is
false.
- The condition is
- Use
useSelectto access the selectors.
The survey should now be triggered in a useEffect with the value of shouldTriggerSurvey as a dependency. This is because the survey timeout state is not guaranteed to be available in the useMount hook, as it may still be resolving at this point.
- Replace
useMountwithuseEffect. - Include
shouldTriggerSurveyin the condition for triggering the survey. - Ensure
shouldTriggerSurveyis included in theuseEffectdependencies.
Update the view_dashboard survey timeouts to one day
- In
DashboardAppandDashboardMainApp, update theview_dashboardTTL value to86400(i.e. one day, in seconds).
Test Coverage
- Add PHP and JS tests for the new endpoints and store.
- Update the tests for
triggerSurveyto reflect the new behaviour. - Update any failing tests.
QA Brief
- Install and activate the
Always Surveysplugin - Go to the dashboard page and confirm that you see the survey. Don’t do anything for a minute and then refresh the page.
- Confirm that you don’t see the survey anymore and it doesn’t re-appear during the next 24 hours.
Changelog entry
- Improve logic for handling timeouts for user surveys on the dashboard.
Issue Analytics
- State:
- Created 2 years ago
- Comments:11 (2 by maintainers)

Top Related StackOverflow Question
Ah, sure thing. I actually figured since the issue was already QA’d as-shipped in
mainthat it was safer to leave this one indevelop-only, but fair enough: that confuses things. 😅I’ve approved that
main-targetting PR as well, thanks!@felixarntz, yes, you are right. Sorry about that. PR is approved.