diff --git a/services/web/app/src/Features/Project/ProjectController.mjs b/services/web/app/src/Features/Project/ProjectController.mjs index 2e349ae40a..ded4afd98c 100644 --- a/services/web/app/src/Features/Project/ProjectController.mjs +++ b/services/web/app/src/Features/Project/ProjectController.mjs @@ -807,6 +807,12 @@ const _ProjectController = { userIsMemberOfGroupSubscription ) + AnalyticsManager.setUserPropertyForUserInBackground( + userId, + 'customer-io-integration', + true + ) + const template = detachRole === 'detached' ? 'project/ide-react-detached' @@ -845,15 +851,6 @@ const _ProjectController = { fullFeatureSet?.aiErrorAssistant) && !assistantDisabled - const customerIoEnabled = - await SplitTestHandler.promises.hasUserBeenAssignedToVariant( - req, - userId, - 'customer-io-trial-conversion', - 'enabled', - true - ) - const addonPrices = isOverleafAssistBundleEnabled && (await ProjectController._getAddonPrices(req, res)) @@ -971,7 +968,7 @@ const _ProjectController = { isSaas: Features.hasFeature('saas'), shouldLoadHotjar, isOverleafAssistBundleEnabled, - customerIoEnabled, + customerIoEnabled: true, addonPrices, compileSettings: { compileTimeout: ownerFeatures?.compileTimeout, diff --git a/services/web/app/src/Features/Project/ProjectListController.mjs b/services/web/app/src/Features/Project/ProjectListController.mjs index 7ef572091f..d1d04f6cc7 100644 --- a/services/web/app/src/Features/Project/ProjectListController.mjs +++ b/services/web/app/src/Features/Project/ProjectListController.mjs @@ -179,6 +179,13 @@ async function projectListPage(req, res, next) { user.refProviders = _.mapValues(user.refProviders, Boolean) + let onboardingDataCollection + let customerIoEnabled = false + let subjectArea + let usedLatex + let primaryOccupation + let role + if (isSaas) { await SplitTestSessionHandler.promises.sessionMaintenance(req, user) @@ -227,6 +234,26 @@ async function projectListPage(req, res, next) { ) { return res.redirect('/user/emails/primary-email-check') } + + onboardingDataCollection = await OnboardingDataCollection.findById( + userId, + 'subjectArea usedLatex primaryOccupation role' + ) + + if (onboardingDataCollection) { + subjectArea = onboardingDataCollection.subjectArea + usedLatex = onboardingDataCollection.usedLatex + primaryOccupation = onboardingDataCollection.primaryOccupation + role = onboardingDataCollection.role + } + + customerIoEnabled = true + + AnalyticsManager.setUserPropertyForUserInBackground( + userId, + 'customer-io-integration', + true + ) } const tags = await TagsHandler.promises.getAllTags(userId) @@ -274,6 +301,10 @@ async function projectListPage(req, res, next) { return result }) + const commonsInstitution = userAffiliations.find( + affiliation => affiliation.institution?.commonsAccount + )?.institution?.name + const portalTemplates = _buildPortalTemplatesList(userAffiliations) const { allInReconfirmNotificationPeriods } = userEmailsData @@ -474,64 +505,9 @@ async function projectListPage(req, res, next) { logger.error({ err: error }, 'Failed to get individual subscription') } - const affiliations = userAffiliations || [] - const commonsInstitution = affiliations.find( - affiliation => affiliation.institution?.commonsAccount - )?.institution?.name - - let onboardingDataCollection - let subjectArea - let usedLatex - let primaryOccupation - let role - - // customer.io: Premium nudge experiment - // Only do customer-io-trial-conversion assignment for users not in India/China and not in group/commons - let customerIoEnabled = false const aiBlocked = !(await _canUseAIAssist(user)) const hasAiAssist = await _userHasAIAssist(user) - if (!userIsMemberOfGroupSubscription && !commonsInstitution && isSaas) { - try { - const excludedCountries = ['IN', 'CN'] - - if (!excludedCountries.includes(countryCode)) { - const cioAssignment = - await SplitTestHandler.promises.getAssignmentForUser( - userId, - 'customer-io-trial-conversion' - ) - if (cioAssignment.variant === 'enabled') { - customerIoEnabled = true - onboardingDataCollection = await OnboardingDataCollection.findById( - userId, - 'subjectArea usedLatex primaryOccupation role' - ) - - if (onboardingDataCollection) { - subjectArea = onboardingDataCollection.subjectArea - usedLatex = onboardingDataCollection.usedLatex - primaryOccupation = onboardingDataCollection.primaryOccupation - role = onboardingDataCollection.role - } - - AnalyticsManager.setUserPropertyForUserInBackground( - userId, - 'customer-io-integration', - true - ) - } - } - } catch (err) { - logger.error( - { err }, - 'Error checking geo location for customer-io-trial-conversion' - ) - // Fallback to not enabled if geoip fails - customerIoEnabled = false - } - } - await SplitTestHandler.promises.getAssignment( req, res, diff --git a/services/web/app/src/Features/Subscription/RecurlyEventHandler.mjs b/services/web/app/src/Features/Subscription/RecurlyEventHandler.mjs index 422038a66f..1ef6e95319 100644 --- a/services/web/app/src/Features/Subscription/RecurlyEventHandler.mjs +++ b/services/web/app/src/Features/Subscription/RecurlyEventHandler.mjs @@ -1,4 +1,3 @@ -import SplitTestHandler from '../SplitTests/SplitTestHandler.mjs' import AnalyticsManager from '../Analytics/AnalyticsManager.mjs' import SubscriptionEmailHandler from './SubscriptionEmailHandler.mjs' import { AI_ADD_ON_CODE } from './AiHelper.mjs' @@ -15,6 +14,8 @@ async function sendRecurlyAnalyticsEvent(event, eventData) { return } + eventData['customerio-integration'] = true + const subscription = await SubscriptionLocator.promises.getUsersSubscription(userId) @@ -26,16 +27,6 @@ async function sendRecurlyAnalyticsEvent(event, eventData) { return } - const customerIoEnabled = - await SplitTestHandler.promises.hasUserBeenAssignedToVariant( - {}, - userId, - 'customer-io-trial-conversion', - 'enabled', - true - ) - eventData['customerio-integration'] = customerIoEnabled || false - switch (event) { case 'new_subscription_notification': await _sendSubscriptionStartedEvent(userId, eventData) @@ -158,17 +149,6 @@ async function _sendSubscriptionStartedEvent(userId, eventData) { if (isTrial) { await SubscriptionEmailHandler.sendTrialOnboardingEmail(userId, planCode) - const cioAssignment = await SplitTestHandler.promises.getAssignmentForUser( - userId, - 'customer-io-trial-conversion' - ) - if (cioAssignment.variant === 'enabled') { - AnalyticsManager.setUserPropertyForUserInBackground( - userId, - 'customer-io-integration', - true - ) - } } } diff --git a/services/web/test/unit/src/Project/ProjectController.test.mjs b/services/web/test/unit/src/Project/ProjectController.test.mjs index 4382654ce9..8efd854dd5 100644 --- a/services/web/test/unit/src/Project/ProjectController.test.mjs +++ b/services/web/test/unit/src/Project/ProjectController.test.mjs @@ -417,6 +417,7 @@ describe('ProjectController', function () { () => ({ default: { recordEventForUserInBackground: () => {}, + setUserPropertyForUserInBackground: () => {}, }, }) ) diff --git a/services/web/test/unit/src/Project/ProjectListController.test.mjs b/services/web/test/unit/src/Project/ProjectListController.test.mjs index bdff939b83..706f808c83 100644 --- a/services/web/test/unit/src/Project/ProjectListController.test.mjs +++ b/services/web/test/unit/src/Project/ProjectListController.test.mjs @@ -10,7 +10,11 @@ const MODULE_PATH = `${import.meta.dirname}/../../../../app/src/Features/Project // Mock AnalyticsManager as it isn't used in these tests but causes the User model to be imported and redeclares queues vi.mock('../../../../app/src/Features/Analytics/AnalyticsManager.mjs', () => { - return {} + return { + default: { + setUserPropertyForUserInBackground: () => {}, + }, + } }) describe('ProjectListController', function () { diff --git a/services/web/test/unit/src/Subscription/RecurlyEventHandler.test.mjs b/services/web/test/unit/src/Subscription/RecurlyEventHandler.test.mjs index 58eb6cfd44..ebc9e9d082 100644 --- a/services/web/test/unit/src/Subscription/RecurlyEventHandler.test.mjs +++ b/services/web/test/unit/src/Subscription/RecurlyEventHandler.test.mjs @@ -120,47 +120,6 @@ describe('RecurlyEventHandler', function () { }) it('with new_subscription_notification - free trial', async function (ctx) { - await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent( - 'new_subscription_notification', - ctx.eventData - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.recordEventForUserInBackground, - ctx.userId, - 'subscription-started', - { - plan_code: ctx.planCode, - quantity: 1, - is_trial: true, - has_ai_add_on: false, - subscriptionId: ctx.eventData.subscription.uuid, - payment_provider: 'recurly', - 'customerio-integration': false, - } - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.setUserPropertyForUserInBackground, - ctx.userId, - 'subscription-plan-code', - ctx.planCode - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.setUserPropertyForUserInBackground, - ctx.userId, - 'subscription-state', - 'active' - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.setUserPropertyForUserInBackground, - ctx.userId, - 'subscription-is-trial', - true - ) - }) - - it('with new_subscription_notification - free trial with customerio integration enabled', async function (ctx) { - ctx.SplitTestHandler.promises.hasUserBeenAssignedToVariant.resolves(true) - await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent( 'new_subscription_notification', ctx.eventData @@ -232,7 +191,7 @@ describe('RecurlyEventHandler', function () { has_ai_add_on: false, subscriptionId: ctx.eventData.subscription.uuid, payment_provider: 'recurly', - 'customerio-integration': false, + 'customerio-integration': true, } ) sinon.assert.calledWith( @@ -252,49 +211,6 @@ describe('RecurlyEventHandler', function () { it('with updated_subscription_notification', async function (ctx) { ctx.planCode = 'new-plan-code' ctx.eventData.subscription.plan.plan_code = ctx.planCode - await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent( - 'updated_subscription_notification', - ctx.eventData - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.recordEventForUserInBackground, - ctx.userId, - 'subscription-updated', - { - plan_code: ctx.planCode, - quantity: 1, - is_trial: true, - has_ai_add_on: false, - subscriptionId: ctx.eventData.subscription.uuid, - payment_provider: 'recurly', - 'customerio-integration': false, - } - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.setUserPropertyForUserInBackground, - ctx.userId, - 'subscription-plan-code', - ctx.planCode - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.setUserPropertyForUserInBackground, - ctx.userId, - 'subscription-state', - 'active' - ) - sinon.assert.calledWith( - ctx.AnalyticsManager.setUserPropertyForUserInBackground, - ctx.userId, - 'subscription-is-trial', - true - ) - }) - - it('with updated_subscription_notification with customerio integration enabled', async function (ctx) { - ctx.SplitTestHandler.promises.hasUserBeenAssignedToVariant.resolves(true) - ctx.planCode = 'new-plan-code' - ctx.eventData.subscription.plan.plan_code = ctx.planCode - await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent( 'updated_subscription_notification', ctx.eventData @@ -350,7 +266,7 @@ describe('RecurlyEventHandler', function () { has_ai_add_on: false, subscriptionId: ctx.eventData.subscription.uuid, payment_provider: 'recurly', - 'customerio-integration': false, + 'customerio-integration': true, } ) sinon.assert.calledWith( @@ -384,7 +300,7 @@ describe('RecurlyEventHandler', function () { has_ai_add_on: false, subscriptionId: ctx.eventData.subscription.uuid, payment_provider: 'recurly', - 'customerio-integration': false, + 'customerio-integration': true, } ) sinon.assert.calledWith( @@ -423,7 +339,7 @@ describe('RecurlyEventHandler', function () { has_ai_add_on: false, subscriptionId: ctx.eventData.subscription.uuid, payment_provider: 'recurly', - 'customerio-integration': false, + 'customerio-integration': true, } ) }) @@ -443,7 +359,7 @@ describe('RecurlyEventHandler', function () { has_ai_add_on: false, subscriptionId: ctx.eventData.subscription.uuid, payment_provider: 'recurly', - 'customerio-integration': false, + 'customerio-integration': true, } ) })