diff --git a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackAction.kt b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackAction.kt index 04c8f4076ec6..0424489386ee 100644 --- a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackAction.kt +++ b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackAction.kt @@ -26,7 +26,7 @@ internal class CsatFeedbackNextDayAction : AnAction(), ActionRemoteBehaviorSpeci NotificationGroupManager.getInstance().getNotificationGroup("System Messages") .createNotification( - "Next CSAT feedback day is " + nextDate.date.format(DateTimeFormatter.ISO_DATE) + + "Next CSAT feedback day is " + nextDate.date.format(DateTimeFormatter.ISO_DATE) + ". " + "User is${if (!nextDate.isNewUser) " not " else " "}new.", NotificationType.INFORMATION ) diff --git a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackSurvey.kt b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackSurvey.kt index 79303f0530fc..d72f990b68bd 100644 --- a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackSurvey.kt +++ b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatFeedbackSurvey.kt @@ -6,6 +6,9 @@ import com.intellij.idea.AppMode import com.intellij.internal.statistic.eventLog.fus.MachineIdManager import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.application.ConfigImportHelper +import com.intellij.openapi.client.ClientKind +import com.intellij.openapi.client.currentSessionOrNull +import com.intellij.openapi.client.sessions import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.util.registry.Registry @@ -26,8 +29,6 @@ import kotlin.math.abs internal const val USER_CONSIDERED_NEW_DAYS = 30 internal const val NEW_USER_SURVEY_PERIOD = 29 internal const val EXISTING_USER_SURVEY_PERIOD = 97 -internal const val CSAT_SURVEY_LAST_FEEDBACK_DATE_KEY = "csat.survey.last.feedback.date" -internal const val CSAT_SURVEY_LAST_NOTIFICATION_DATE_KEY = "csat.survey.last.notification.date" internal class CsatFeedbackSurvey : FeedbackSurvey() { override val feedbackSurveyType: InIdeFeedbackSurveyType = @@ -54,10 +55,19 @@ internal class CsatFeedbackSurveyConfig : InIdeFeedbackSurveyConfig { } override fun updateStateAfterDialogClosedOk(project: Project) { - PropertiesComponent.getInstance().setValue(CSAT_SURVEY_LAST_FEEDBACK_DATE_KEY, getCsatToday().format(DateTimeFormatter.ISO_LOCAL_DATE)) + CsatGlobalSettings.getInstance().lastFeedbackDate = getCsatToday().format(DateTimeFormatter.ISO_LOCAL_DATE) } override fun checkExtraConditionSatisfied(project: Project): Boolean { + if (project.currentSessionOrNull?.isGuest == true) { + LOG.debug("We are a CWM guest, do not really need CSAT") + return false + } + if (project.sessions(ClientKind.GUEST).isNotEmpty()) { + LOG.debug("We are the CWM host at the moment, not the perfect time for CSAT") + return false + } + if (ConfigImportHelper.isFirstSession()) { LOG.debug("It's a first user session, skip the survey") return false @@ -66,7 +76,7 @@ internal class CsatFeedbackSurveyConfig : InIdeFeedbackSurveyConfig { val today = getCsatToday() LOG.debug("Today is ${today.format(DateTimeFormatter.ISO_LOCAL_DATE)}") - PropertiesComponent.getInstance().getValue(CSAT_SURVEY_LAST_NOTIFICATION_DATE_KEY) + CsatGlobalSettings.getInstance().lastNotificationDate ?.let { tryParseDate(it) } ?.let { if (it.isEqual(today)) { @@ -75,7 +85,7 @@ internal class CsatFeedbackSurveyConfig : InIdeFeedbackSurveyConfig { } } - val lastFeedbackDate = PropertiesComponent.getInstance().getValue(CSAT_SURVEY_LAST_FEEDBACK_DATE_KEY) + val lastFeedbackDate = CsatGlobalSettings.getInstance().lastFeedbackDate ?.let { tryParseDate(it) } if (lastFeedbackDate != null && lastFeedbackDate.plusDays(EXISTING_USER_SURVEY_PERIOD.toLong()).isAfter(today)) { LOG.debug("User recently filled the survey, vacation period is in progress") @@ -116,10 +126,10 @@ internal class CsatFeedbackSurveyConfig : InIdeFeedbackSurveyConfig { } override fun updateStateAfterNotificationShowed(project: Project) { - val propertiesComponent = PropertiesComponent.getInstance() - propertiesComponent.setValue(CSAT_SURVEY_LAST_NOTIFICATION_DATE_KEY, getCsatToday().format(DateTimeFormatter.ISO_LOCAL_DATE)) + CsatGlobalSettings.getInstance().lastNotificationDate = getCsatToday().format(DateTimeFormatter.ISO_LOCAL_DATE) + // disable an automatic Evaluate Feedback form so we don't have them both shown - propertiesComponent.setValue("evaluation.feedback.enabled", "false") + PropertiesComponent.getInstance().setValue("evaluation.feedback.enabled", "false") } } diff --git a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatGlobalSettings.kt b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatGlobalSettings.kt new file mode 100644 index 000000000000..2bee33cc2f43 --- /dev/null +++ b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatGlobalSettings.kt @@ -0,0 +1,48 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.platform.feedback.csat + +import com.intellij.frontend.HostIdeInfoService +import com.intellij.ide.Prefs +import com.intellij.openapi.application.ApplicationInfo +import com.intellij.openapi.components.service + +private const val CSAT_NEW_USER_CREATED_AT_KEY = "csat.user.created.at" +private const val CSAT_SURVEY_LAST_FEEDBACK_DATE_KEY = "csat.survey.last.feedback.date" +private const val CSAT_SURVEY_LAST_NOTIFICATION_DATE_KEY = "csat.survey.last.notification.date" + +internal class CsatGlobalSettings private constructor( + private val productCode: String, +) { + companion object { + fun getInstance(): CsatGlobalSettings { + return CsatGlobalSettings(service().getHostInfo()?.productCode + ?: ApplicationInfo.getInstance().build.productCode) + } + } + + private fun productKey(keyName: String): String = "JetBrains.$productCode.$keyName" + + var lastFeedbackDate: String? + get() = Prefs.get(productKey(CSAT_SURVEY_LAST_FEEDBACK_DATE_KEY), null) + set(value) { + val key = productKey(CSAT_SURVEY_LAST_FEEDBACK_DATE_KEY) + Prefs.put(key, value) + Prefs.flush(key) + } + + var lastNotificationDate: String? + get() = Prefs.get(productKey(CSAT_SURVEY_LAST_NOTIFICATION_DATE_KEY), null) + set(value) { + val key = productKey(CSAT_SURVEY_LAST_NOTIFICATION_DATE_KEY) + Prefs.put(key, value) + Prefs.flush(key) + } + + var newUserCreatedAt: String? + get() = Prefs.get(productKey(CSAT_NEW_USER_CREATED_AT_KEY), null) + set(value) { + val key = productKey(CSAT_NEW_USER_CREATED_AT_KEY) + Prefs.put(key, value) + Prefs.flush(key) + } +} diff --git a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatNewUserTracker.kt b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatNewUserTracker.kt index 6c406e6dcc2a..c4f60e5d7eb8 100644 --- a/platform/feedback/src/com/intellij/platform/feedback/csat/CsatNewUserTracker.kt +++ b/platform/feedback/src/com/intellij/platform/feedback/csat/CsatNewUserTracker.kt @@ -1,7 +1,6 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.platform.feedback.csat -import com.intellij.ide.util.PropertiesComponent import com.intellij.openapi.application.ConfigImportHelper import com.intellij.openapi.project.Project import com.intellij.openapi.startup.ProjectActivity @@ -9,16 +8,12 @@ import com.intellij.openapi.util.registry.Registry import java.time.LocalDate import java.time.format.DateTimeFormatter.ISO_LOCAL_DATE -internal const val CSAT_NEW_USER_CREATED_AT_PROPERTY = "csat.user.created.at" - internal class CsatNewUserTracker : ProjectActivity { override suspend fun execute(project: Project) { if (ConfigImportHelper.isNewUser()) { - val propertiesComponent = PropertiesComponent.getInstance() - - if (!propertiesComponent.isValueSet(CSAT_NEW_USER_CREATED_AT_PROPERTY)) { - propertiesComponent.setValue(CSAT_NEW_USER_CREATED_AT_PROPERTY, - LocalDate.now().format(ISO_LOCAL_DATE)) + val settings = CsatGlobalSettings.getInstance() + if (settings.newUserCreatedAt == null) { + settings.newUserCreatedAt = LocalDate.now().format(ISO_LOCAL_DATE) } } } @@ -30,6 +25,6 @@ internal fun getCsatUserCreatedDate(): LocalDate? { return tryParseDate(mocked) } - val date = PropertiesComponent.getInstance().getValue(CSAT_NEW_USER_CREATED_AT_PROPERTY) ?: return null + val date = CsatGlobalSettings.getInstance().newUserCreatedAt ?: return null return tryParseDate(date) } \ No newline at end of file