diff --git a/src/lib/user-guiding.js b/src/lib/user-guiding.js
new file mode 100644
index 000000000..8612c68ec
--- /dev/null
+++ b/src/lib/user-guiding.js
@@ -0,0 +1,163 @@
+const api = require('./api');
+
+const USER_GUIDING_ID = process.env.USER_GUIDING_ID;
+const SCRIPT_ID = 'UserGuiding';
+
+const CONDITIONS = {condition_list: [
+ 'IsLoggedIn',
+ 'IsNewScratcher',
+ '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: []
+ };
+ 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;
+ }
+ }
+};
+
+const activateUserGuiding = (userId, callback) => {
+ if (!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) {
+ callback();
+ }
+};
+
+const displayUserGuiding = (userId, permissions, guideId, callback) => (
+ api({
+ uri: '/user_guiding',
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-USERID': userId,
+ 'X-PERMISSIONS': JSON.stringify(permissions),
+ 'X-CONDITIONS': JSON.stringify(CONDITIONS),
+ 'X-QUESTION-NUMBER': guideId
+ },
+ host: process.env.SORTING_HAT_HOST
+ }, (err, body, res) => {
+ if (err || res.statusCode !== 200) {
+ return;
+ }
+
+ const {result} = JSON.stringify(body);
+ if (result) {
+ 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(
+ userId,
+ permissions,
+ AUTONOMY_SURVEY_ID,
+ () => launchSurvey(AUTONOMY_SURVEY_ID)
+ );
+};
+
+const loadRelationshipsSurvey = (userId, permissions) => {
+ const RELATIONSHIP_SURVEY_ID = 3049;
+
+ displayUserGuiding(
+ userId,
+ permissions,
+ RELATIONSHIP_SURVEY_ID,
+ () => launchSurvey(RELATIONSHIP_SURVEY_ID)
+ );
+};
+
+const loadJoySurvey = (userId, permissions) => {
+ const JOY_SURVEY_ID = 3050;
+
+ displayUserGuiding(
+ userId,
+ permissions,
+ JOY_SURVEY_ID,
+ () => launchSurvey(JOY_SURVEY_ID)
+ );
+};
+
+const loadRandomPrompt = (userId, permissions, data) => {
+ const prompt = probabilityPicker(data);
+
+ prompt(userId, permissions);
+};
+
+module.exports = {
+ loadCompetenceSurvey,
+ loadAutonomySurvey,
+ loadRelationshipsSurvey,
+ loadJoySurvey,
+ loadRandomPrompt
+};
diff --git a/src/views/preview/comment/compose-comment.jsx b/src/views/preview/comment/compose-comment.jsx
index cfa706dec..adba435d6 100644
--- a/src/views/preview/comment/compose-comment.jsx
+++ b/src/views/preview/comment/compose-comment.jsx
@@ -360,6 +360,7 @@ class ComposeComment extends React.Component {
type="textarea"
value={this.state.message}
onInput={this.handleInput}
+ onClick={this.props.onClick}
autoFocus={this.props.isReply}
/>
@@ -437,6 +438,7 @@ 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 a1578d102..ced00b5da 100644
--- a/src/views/preview/presentation.jsx
+++ b/src/views/preview/presentation.jsx
@@ -34,11 +34,13 @@ 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 projectShape = require('./projectshape.jsx').projectShape;
require('./preview.scss');
const frameless = require('../../lib/frameless');
+const {useState, useCallback} = require('react');
// disable enter key submission on formsy input fields; otherwise formsy thinks
// we meant to trigger the "See inside" button. Instead, treat these keypresses
@@ -127,6 +129,7 @@ const PreviewPresentation = ({
showCloudDataAlert,
showCloudDataAndVideoAlert,
showUsernameBlockAlert,
+ permissions,
projectHost,
projectId,
projectInfo,
@@ -140,9 +143,11 @@ const PreviewPresentation = ({
showEmailConfirmationBanner,
singleCommentId,
socialOpen,
+ user,
userOwnsProject,
visibilityInfo
}) => {
+ const [interactionWithComment, setInteractionWithComment] = 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 ||
@@ -215,6 +220,14 @@ const PreviewPresentation = ({
))}
);
+
+ const onCommentClick = useCallback(() => {
+ if (!interactionWithComment && user) {
+ setInteractionWithComment(!interactionWithComment);
+ loadAutonomySurvey(user.id, permissions);
+ }
+ }, [interactionWithComment, user]);
+
return (
{showEmailConfirmationModal &&
) : (
/* TODO add box for signing in to leave a comment */
@@ -784,6 +798,7 @@ PreviewPresentation.propTypes = {
onUpdateProjectThumbnail: PropTypes.func,
originalInfo: projectShape,
parentInfo: projectShape,
+ permissions: PropTypes.object,
projectHost: PropTypes.string,
projectId: PropTypes.string,
projectInfo: projectShape,
@@ -800,6 +815,17 @@ PreviewPresentation.propTypes = {
showUsernameBlockAlert: PropTypes.bool,
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
+ }),
userOwnsProject: PropTypes.bool,
visibilityInfo: PropTypes.shape({
censored: PropTypes.bool,
diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx
index bc53feb39..bd06654c6 100644
--- a/src/views/preview/project-view.jsx
+++ b/src/views/preview/project-view.jsx
@@ -25,6 +25,12 @@ const ConnectedLogin = require('../../components/login/connected-login.jsx');
const CanceledDeletionModal = require('../../components/login/canceled-deletion-modal.jsx');
const NotAvailable = require('../../components/not-available/not-available.jsx');
const Meta = require('./meta.jsx');
+const {
+ loadRelationshipsSurvey,
+ loadCompetenceSurvey,
+ loadJoySurvey,
+ loadRandomPrompt
+} = require('../../lib/user-guiding.js');
const sessionActions = require('../../redux/session.js');
const {selectProjectCommentsGloballyEnabled, selectIsTotallyNormal} = require('../../redux/session');
@@ -83,6 +89,7 @@ class Preview extends React.Component {
'handleUpdateProjectId',
'handleUpdateProjectTitle',
'handleToggleComments',
+ 'handleLoadRandomPrompt',
'initCounts',
'pushHistory',
'renderLogin',
@@ -627,6 +634,7 @@ class Preview extends React.Component {
justRemixed: false,
justShared: true
});
+ loadRelationshipsSurvey(this.props.user.id, this.props.permissions);
}
handleShareAttempt () {
this.setState({
@@ -694,6 +702,18 @@ 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,
@@ -786,6 +806,7 @@ class Preview extends React.Component {
moreCommentsToLoad={this.props.moreCommentsToLoad}
originalInfo={this.props.original}
parentInfo={this.props.parent}
+ permissions={this.props.permissions}
projectHost={this.props.projectHost}
projectId={this.state.projectId}
projectInfo={this.props.projectInfo}
@@ -802,6 +823,7 @@ class Preview extends React.Component {
showUsernameBlockAlert={this.state.showUsernameBlockAlert}
singleCommentId={this.state.singleCommentId}
socialOpen={this.state.socialOpen}
+ user={this.props.user}
userOwnsProject={this.props.userOwnsProject}
visibilityInfo={this.props.visibilityInfo}
onAddComment={this.handleAddComment}
@@ -879,6 +901,7 @@ class Preview extends React.Component {
onUpdateProjectId={this.handleUpdateProjectId}
onUpdateProjectThumbnail={this.props.handleUpdateProjectThumbnail}
onUpdateProjectTitle={this.handleUpdateProjectTitle}
+ onLoadRandomPrompt={this.handleLoadRandomPrompt}
/>
)}
{this.props.registrationOpen && (
@@ -957,6 +980,7 @@ Preview.propTypes = {
moreCommentsToLoad: PropTypes.bool,
original: projectShape,
parent: projectShape,
+ permissions: PropTypes.object,
playerMode: PropTypes.bool,
projectHost: PropTypes.string.isRequired,
projectInfo: projectShape,
@@ -1076,6 +1100,7 @@ const mapStateToProps = state => {
moreCommentsToLoad: state.comments.moreCommentsToLoad,
original: state.preview.original,
parent: state.preview.parent,
+ permissions: state.permissions,
playerMode: state.scratchGui.mode.isPlayerOnly,
projectInfo: state.preview.projectInfo,
projectNotAvailable: state.preview.projectNotAvailable,
diff --git a/webpack.config.js b/webpack.config.js
index 61db55450..aec11a587 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -279,7 +279,9 @@ module.exports = {
'process.env.DEBUG': Boolean(process.env.DEBUG),
'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`,
'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.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'}"`
})
])
.concat(process.env.ANALYZE_BUNDLE === 'true' ? [