From 6fb0e3491d721b1fa98a8d2cad7acd2ecf8aacd3 Mon Sep 17 00:00:00 2001 From: Aleksandar Shumakov <a.shumakov97@gmail.com> Date: Thu, 22 Aug 2024 15:28:54 +0300 Subject: [PATCH] feat: simplify user guiding triggers - Change exposed handler names to correspond to performed action - Simplify method of picking random survey on interaction with Editor - Move GUI useEffect hook to WWW in IntlGUIWithProjectHandler - Remove onClick property from ComposeComment component --- package-lock.json | 7 + package.json | 1 + src/lib/user-guiding.js | 170 +++++++----------- src/views/preview/comment/compose-comment.jsx | 2 - src/views/preview/presentation.jsx | 30 ++-- src/views/preview/project-view.jsx | 45 ++--- webpack.config.js | 2 +- 7 files changed, 111 insertions(+), 146 deletions(-) diff --git a/package-lock.json b/package-lock.json index de7ae7809..538ddba95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,6 +80,7 @@ "lodash.merge": "4.6.2", "lodash.mergewith": "4.6.2", "lodash.omit": "3.1.0", + "lodash.sample": "4.2.1", "lodash.uniqby": "4.7.0", "mini-css-extract-plugin": "1.6.2", "minilog": "2.1.0", @@ -17081,6 +17082,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.sample": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.sample/-/lodash.sample-4.2.1.tgz", + "integrity": "sha512-odCZufa8jYDBZQ+JOSePWRs+iApPdvIp3qAiKc9F22RdSCMLuUu60Jvgs2M6qMisKAeBZoumAkqDiGr9HDym/Q==", + "dev": true + }, "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", diff --git a/package.json b/package.json index e12d644e1..261a4503d 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "lodash.mergewith": "4.6.2", "lodash.omit": "3.1.0", "lodash.uniqby": "4.7.0", + "lodash.sample": "4.2.1", "mini-css-extract-plugin": "1.6.2", "minilog": "2.1.0", "pako": "0.2.8", diff --git a/src/lib/user-guiding.js b/src/lib/user-guiding.js index c899d86e0..0cea4d82b 100644 --- a/src/lib/user-guiding.js +++ b/src/lib/user-guiding.js @@ -1,7 +1,12 @@ const api = require('./api'); +const sample = require('lodash.sample'); const USER_GUIDING_ID = process.env.USER_GUIDING_ID; -const SCRIPT_ID = 'UserGuiding'; +const AUTONOMY_SURVEY_ID = 3048; +const RELATIONSHIP_SURVEY_ID = 3049; +const JOY_SURVEY_ID = 3050; +const COMPETENCE_SURVEY_ID = 3045; +const EDITOR_INTERACTION_SURVEY_IDS = [COMPETENCE_SURVEY_ID, JOY_SURVEY_ID]; const CONDITIONS = {condition_list: [ 'IsLoggedIn', @@ -9,78 +14,58 @@ const CONDITIONS = {condition_list: [ 'NotMuted' ]}; -const getUserGuidingSnippet = () => ( - ` - (function(g, u, i, d, e, s) { - g[e] = g[e] || []; - var f = u.getElementsByTagName(i)[0]; - var k = u.createElement(i); - k.async = true; - k.src = 'https://static.userguiding.com/media/user-guiding-' + s + '-embedded.js'; - f.parentNode.insertBefore(k, f); - if (g[d]) return; - var ug = g[d] = { - q: [] +const USER_GUIDING_SNIPPET = ` + (function(g, u, i, d, e, s) { + g[e] = g[e] || []; + var f = u.getElementsByTagName(i)[0]; + var k = u.createElement(i); + k.async = true; + k.src = 'https://static.userguiding.com/media/user-guiding-' + s + '-embedded.js'; + f.parentNode.insertBefore(k, f); + if (g[d]) return; + var ug = g[d] = { + q: [] + }; + ug.c = function(n) { + return function() { + ug.q.push([n, arguments]) }; - ug.c = function(n) { - return function() { - ug.q.push([n, arguments]) - }; - }; - var m = ['previewGuide', 'finishPreview', 'track', 'identify', 'hideChecklist', 'launchChecklist']; - for (var j = 0; j < m.length; j += 1) { - ug[m[j]] = ug.c(m[j]); - } - })(window, document, 'script', 'userGuiding', 'userGuidingLayer', '${USER_GUIDING_ID}'); - ` -); - -const identifyUser = userId => { - window.userGuiding.identify(userId.toString()); -}; - -const launchSurvey = surveyId => { - window.userGuiding.launchSurvey(surveyId); -}; - -const probabilityPicker = data => { - let generatedValue = Math.random(); - - for (const tmp of data) { - if (tmp.probability < generatedValue) { - generatedValue -= tmp.probability; - } else { - return tmp.prompt; + }; + var m = ['previewGuide', 'finishPreview', 'track', 'identify', 'hideChecklist', 'launchChecklist']; + for (var j = 0; j < m.length; j += 1) { + ug[m[j]] = ug.c(m[j]); } - } -}; + })(window, document, 'script', 'userGuiding', 'userGuidingLayer', '${USER_GUIDING_ID}'); +`; const activateUserGuiding = (userId, callback) => { - if (USER_GUIDING_ID && !document.getElementById(SCRIPT_ID)) { - const userGuidingScript = document.createElement('script'); - userGuidingScript.id = SCRIPT_ID; - userGuidingScript.innerHTML = getUserGuidingSnippet(); - document.head.insertBefore(userGuidingScript, document.head.firstChild); - - window.userGuidingSettings = { - disablePageViewAutoCapture: true - }; - - window.userGuidingLayer.push({ - event: 'onload', - fn: () => identifyUser(userId) - }); - - window.userGuidingLayer.push({ - event: 'onIdentificationComplete', - fn: callback - }); - } else if (window.userGuiding) { + if (window.userGuiding) { callback(); + return; } + + const userGuidingScript = document.createElement('script'); + userGuidingScript.innerHTML = USER_GUIDING_SNIPPET; + document.head.insertBefore(userGuidingScript, document.head.firstChild); + + window.userGuidingSettings = {disablePageViewAutoCapture: true}; + + window.userGuidingLayer.push({ + event: 'onload', + fn: () => window.userGuiding.identify(userId.toString()) + }); + + window.userGuidingLayer.push({ + event: 'onIdentificationComplete', + fn: callback + }); }; -const displayUserGuiding = (userId, permissions, guideId, callback) => ( +const attemptDisplayUserGuidingSurvey = (userId, permissions, guideId, callback) => { + if (!USER_GUIDING_ID || !process.env.SORTING_HAT_HOST) { + return; + } + api({ uri: '/user_guiding', method: 'GET', @@ -97,66 +82,43 @@ const displayUserGuiding = (userId, permissions, guideId, callback) => ( return; } - if (body?.result === "true") { + if (body?.result === 'true') { activateUserGuiding(userId, callback); } - }) -); - -const loadCompetenceSurvey = (userId, permissions) => { - const COMPETENCE_SURVEY_ID = 3045; - - displayUserGuiding( - userId, - permissions, - COMPETENCE_SURVEY_ID, - () => launchSurvey(COMPETENCE_SURVEY_ID) - ); + }); }; -const loadAutonomySurvey = (userId, permissions) => { - const AUTONOMY_SURVEY_ID = 3048; - - displayUserGuiding( +const onCommented = (userId, permissions) => { + attemptDisplayUserGuidingSurvey( userId, permissions, AUTONOMY_SURVEY_ID, - () => launchSurvey(AUTONOMY_SURVEY_ID) + () => window.userGuiding.launchSurvey(AUTONOMY_SURVEY_ID) ); }; -const loadRelationshipsSurvey = (userId, permissions) => { - const RELATIONSHIP_SURVEY_ID = 3049; - - displayUserGuiding( +const onProjectShared = (userId, permissions) => { + attemptDisplayUserGuidingSurvey( userId, permissions, RELATIONSHIP_SURVEY_ID, - () => launchSurvey(RELATIONSHIP_SURVEY_ID) + () => window.userGuiding.launchSurvey(RELATIONSHIP_SURVEY_ID) ); }; -const loadJoySurvey = (userId, permissions) => { - const JOY_SURVEY_ID = 3050; +const onProjectLoaded = (userId, permissions) => { + const surveyId = sample(EDITOR_INTERACTION_SURVEY_IDS); - displayUserGuiding( + attemptDisplayUserGuidingSurvey( userId, permissions, - JOY_SURVEY_ID, - () => launchSurvey(JOY_SURVEY_ID) + surveyId, + () => window.userGuiding.launchSurvey(surveyId) ); }; -const loadRandomPrompt = (userId, permissions, data) => { - const prompt = probabilityPicker(data); - - prompt(userId, permissions); -}; - module.exports = { - loadCompetenceSurvey, - loadAutonomySurvey, - loadRelationshipsSurvey, - loadJoySurvey, - loadRandomPrompt + onProjectLoaded, + onCommented, + onProjectShared }; diff --git a/src/views/preview/comment/compose-comment.jsx b/src/views/preview/comment/compose-comment.jsx index adba435d6..cfa706dec 100644 --- a/src/views/preview/comment/compose-comment.jsx +++ b/src/views/preview/comment/compose-comment.jsx @@ -360,7 +360,6 @@ class ComposeComment extends React.Component { type="textarea" value={this.state.message} onInput={this.handleInput} - onClick={this.props.onClick} autoFocus={this.props.isReply} /> <FlexRow className="compose-bottom-row"> @@ -438,7 +437,6 @@ ComposeComment.propTypes = { }), onAddComment: PropTypes.func, onCancel: PropTypes.func, - onClick: PropTypes.func, parentId: PropTypes.number, postURI: PropTypes.string, user: PropTypes.shape({ diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx index ced00b5da..6b8e80c03 100644 --- a/src/views/preview/presentation.jsx +++ b/src/views/preview/presentation.jsx @@ -34,7 +34,7 @@ const thumbnailUrl = require('../../lib/user-thumbnail'); const FormsyProjectUpdater = require('./formsy-project-updater.jsx'); const EmailConfirmationModal = require('../../components/modal/email-confirmation/modal.jsx'); const EmailConfirmationBanner = require('../../components/dropdown-banner/email-confirmation/banner.jsx'); -const {loadAutonomySurvey} = require('../../lib/user-guiding.js'); +const {onCommented} = require('../../lib/user-guiding.js'); const projectShape = require('./projectshape.jsx').projectShape; require('./preview.scss'); @@ -147,7 +147,7 @@ const PreviewPresentation = ({ userOwnsProject, visibilityInfo }) => { - const [interactionWithComment, setInteractionWithComment] = useState(false); + const [hasSubmittedComment, setHasSubmittedComment] = useState(false); const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : ''; const revisedDate = ((projectInfo.history && projectInfo.history.modified)) ? projectInfo.history.modified : ''; const showInstructions = editable || projectInfo.instructions || @@ -221,13 +221,14 @@ const PreviewPresentation = ({ </FlexRow> ); - const onCommentClick = useCallback(() => { - if (!interactionWithComment && user) { - setInteractionWithComment(!interactionWithComment); - loadAutonomySurvey(user.id, permissions); + const onAddCommentWrapper = useCallback(body => { + onAddComment(body); + if (!hasSubmittedComment && user) { + setHasSubmittedComment(true); + onCommented(user.id, permissions); } - }, [interactionWithComment, user]); - + }, [hasSubmittedComment, user]); + return ( <div className="preview"> {showEmailConfirmationModal && <EmailConfirmationModal @@ -623,8 +624,7 @@ const PreviewPresentation = ({ isLoggedIn ? ( isShared && <ComposeComment postURI={`/proxy/comments/project/${projectId}`} - onAddComment={onAddComment} - onClick={onCommentClick} + onAddComment={onAddCommentWrapper} /> ) : ( /* TODO add box for signing in to leave a comment */ @@ -816,15 +816,7 @@ PreviewPresentation.propTypes = { singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), socialOpen: PropTypes.bool, user: PropTypes.shape({ - id: PropTypes.number, - banned: PropTypes.bool, - vpn_required: PropTypes.bool, - username: PropTypes.string, - token: PropTypes.string, - thumbnailUrl: PropTypes.string, - dateJoined: PropTypes.string, - email: PropTypes.string, - classroomId: PropTypes.string + id: PropTypes.number }), userOwnsProject: PropTypes.bool, visibilityInfo: PropTypes.shape({ diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index bd06654c6..b42fd67d0 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -26,10 +26,8 @@ const CanceledDeletionModal = require('../../components/login/canceled-deletion- const NotAvailable = require('../../components/not-available/not-available.jsx'); const Meta = require('./meta.jsx'); const { - loadRelationshipsSurvey, - loadCompetenceSurvey, - loadJoySurvey, - loadRandomPrompt + onProjectShared, + onProjectLoaded } = require('../../lib/user-guiding.js'); const sessionActions = require('../../redux/session.js'); @@ -46,6 +44,25 @@ const IntlGUI = injectIntl(GUI.default); const localStorageAvailable = 'localStorage' in window && window.localStorage !== null; const xhr = require('xhr'); +const {useEffect} = require('react'); + +const IntlGUIWithProjectHandler = ({user, permissions, ...props}) => { + useEffect(() => { + if (props.projectId && props.projectId !== '0') { + onProjectLoaded(user.id, permissions); + } + }, [props.projectId, user.id, permissions]); + + return <IntlGUI {...props} />; +}; + +IntlGUIWithProjectHandler.propTypes = { + ...GUI.propTypes, + user: PropTypes.shape({ + id: PropTypes.number + }), + permissions: PropTypes.object +}; class Preview extends React.Component { constructor (props) { @@ -89,7 +106,6 @@ class Preview extends React.Component { 'handleUpdateProjectId', 'handleUpdateProjectTitle', 'handleToggleComments', - 'handleLoadRandomPrompt', 'initCounts', 'pushHistory', 'renderLogin', @@ -634,7 +650,7 @@ class Preview extends React.Component { justRemixed: false, justShared: true }); - loadRelationshipsSurvey(this.props.user.id, this.props.permissions); + onProjectShared(this.props.user.id, this.props.permissions); } handleShareAttempt () { this.setState({ @@ -702,18 +718,6 @@ class Preview extends React.Component { this.props.user.token ); } - handleLoadRandomPrompt () { - loadRandomPrompt(this.props.user.id, this.props.permissions, [ - { - prompt: loadCompetenceSurvey, - probability: 0.5 - }, - { - prompt: loadJoySurvey, - probability: 0.5 - } - ]); - } initCounts (favorites, loves) { this.setState({ favoriteCount: favorites, @@ -863,7 +867,7 @@ class Preview extends React.Component { </Page> : <React.Fragment> {showGUI && ( - <IntlGUI + <IntlGUIWithProjectHandler assetHost={this.props.assetHost} authorId={this.props.authorId} authorThumbnailUrl={this.props.authorThumbnailUrl} @@ -901,7 +905,8 @@ class Preview extends React.Component { onUpdateProjectId={this.handleUpdateProjectId} onUpdateProjectThumbnail={this.props.handleUpdateProjectThumbnail} onUpdateProjectTitle={this.handleUpdateProjectTitle} - onLoadRandomPrompt={this.handleLoadRandomPrompt} + user={this.props.user} + permissions={this.props.permissions} /> )} {this.props.registrationOpen && ( diff --git a/webpack.config.js b/webpack.config.js index aec11a587..8f71b2b00 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -281,7 +281,7 @@ module.exports = { 'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`, 'process.env.GTM_ID': process.env.GTM_ID ? `"${process.env.GTM_ID}"` : null, 'process.env.USER_GUIDING_ID': `"${process.env.USER_GUIDING_ID || ''}"`, - 'process.env.SORTING_HAT_HOST': `"${process.env.SORTING_HAT_HOST || 'http://127.0.0.1:7676'}"` + 'process.env.SORTING_HAT_HOST': `"${process.env.SORTING_HAT_HOST || ''}"` }) ]) .concat(process.env.ANALYZE_BUNDLE === 'true' ? [