import { commands , MessageItem , window } from 'vscode' ; import { callWithTelemetryAndErrorHandling , IActionContext } from "vscode-azureextensionui" ; import { globalStateKeys } from '../common' ; import { ext } from "./extensionVariables" ; import { assert } from './fixed_assert' ; import { httpGet } from "./util/httpGet" ; import { hoursToMs , minutesToMs , weeksToMs } from "./util/time" ; import { parseUri } from './util/uri' ; const linkToSurvey = 'https://aka.ms/arm-tools-survey' ; const surveyPrompt = "Could you please take 2 minutes to tell us how well the Azure Resource Manager Tools extension is working for you?" ; const stateKeys = globalStateKeys . survey ; interface ISurveyConstants { activeUsageMsBeforeSurvey : number ; percentageOfUsersToSurvey : number ; msToPostponeAfterYes : number ; msToPostponeAfterLater : number ; msToPostponeAfterNotSelected : number ; msToPostponeAfterNotAccessible : number ; } const defaultSurveyConstants = { activeUsageMsBeforeSurvey : hoursToMs ( 1 ) , percentageOfUsersToSurvey : 0.25 , msToPostponeAfterYes : weeksToMs ( 12 ) , msToPostponeAfterLater : weeksToMs ( 1 ) , msToPostponeAfterNotSelected : weeksToMs ( 12 ) , msToPostponeAfterNotAccessible : hoursToMs ( 1 ) } ; const debugSurveyConstants = { activeUsageMsBeforeSurvey : minutesToMs ( 1 ) , percentageOfUsersToSurvey : 0.5 , msToPostponeAfterYes : minutesToMs ( 2 ) , msToPostponeAfterLater : minutesToMs ( 1 ) , msToPostponeAfterNotSelected : minutesToMs ( 0.5 ) , msToPostponeAfterNotAccessible : minutesToMs ( 0.25 ) } ; let surveyConstants = defaultSurveyConstants ; let usageSessionStart ; let isReentrant = false ; let surveyDisabled = false ; let isDebugMode = false ; export namespace survey { /**
     * Called whenever the user is interacting with the extension (thus gets called a lot)
     */ export function registerActiveUseNoThrow ( ) { callWithTelemetryAndErrorHandling ( "considerShowingSurvey" , async ( context ) => { context . errorHandling . suppressDisplay = true ; context . telemetry . suppressIfSuccessful = true ; if ( isReentrant ) { return ; } isReentrant = true ; try { await checkForDebugMode ( context ) ; if ( surveyDisabled ) { return ; } const neverShowSurvey = getShouldNeverShowSurvey ( ) ; context . telemetry . properties . neverShowSurvey = String ( neverShowSurvey ) ; if ( neverShowSurvey ) { surveyDisabled = true ; context . telemetry . suppressIfSuccessful = false ; return ; } const sessionLengthMs = getSessionLengthMs ( context ) ; context . telemetry . properties . sessionLength = String ( sessionLengthMs ) ; if ( sessionLengthMs < surveyConstants . activeUsageMsBeforeSurvey ) { return ; } if ( getIsSurveyPostponed ( ) ) { return ; } context . telemetry . suppressIfSuccessful = false ; const accessible = await getIsSurveyAccessible ( ) ; context . telemetry . properties . accessible = String ( accessible ) ; if ( ! accessible ) { await postponeSurvey ( context , surveyConstants . msToPostponeAfterNotAccessible ) ; return ; } const isSelected = getIsUserSelected ( ) ; context . telemetry . properties . isSelected = String ( isSelected ) ; if ( isSelected ) { await requestTakeSurvey ( context ) ; } else { await postponeSurvey ( context , surveyConstants . msToPostponeAfterNotSelected ) ; } } finally { isReentrant = false ; } } ) . catch ( err => { assert . fail ( "callWithTelemetryAndErrorHandling in survey.registerActiveUseNoThrow shouldn't throw" ) ; } ) ; } } async function checkForDebugMode ( context ) { if ( ! isDebugMode ) { if ( ext . configuration . get < boolean > ( 'debugSurvey' ) ) { isDebugMode = true ; surveyConstants = debugSurveyConstants ; surveyDisabled = false ; await ext . context . globalState . update ( stateKeys . neverShowSurvey , false ) ; } } if ( isDebugMode ) { context . telemetry . properties . debugMode = "true" ; } } async function requestTakeSurvey ( context ) { const neverAsk = { title : "Never ask again" } ; const later = { title : "Later" } ; const yes = { title : "Yes" } ; const dismissed = { title : "(dismissed)" } ; const response = await window . showInformationMessage ( surveyPrompt , neverAsk , later , yes ) ?? dismissed ; context . telemetry . properties . response = String ( response . title ) ; if ( response === neverAsk ) { await ext . context . globalState . update ( stateKeys . neverShowSurvey , true ) ; } else if ( response === later ) { await postponeSurvey ( context , surveyConstants . msToPostponeAfterLater ) ; } else if ( response === yes ) { await postponeSurvey ( context , surveyConstants . msToPostponeAfterYes ) ; await launchSurvey ( context ) ; } else { assert ( response === dismissed , `Unexpected response: ${ response . title }` ) ; await postponeSurvey ( context , surveyConstants . msToPostponeAfterLater ) ; } } async function launchSurvey ( context ) { await commands . executeCommand ( 'vscode.open' , parseUri ( linkToSurvey ) ) ; } function getIsUserSelected ( ) { return Math . random ( ) < surveyConstants . percentageOfUsersToSurvey ; } async function postponeSurvey ( context , milliseconds ) { assert ( milliseconds > 0 ) ; let untilTimeMs = Date . now ( ) + milliseconds ; const currentPostpone = ext . context . globalState . get < number > ( stateKeys . surveyPostponedUntilTime , 0 ) ; if ( Number . isInteger ( currentPostpone ) ) { untilTimeMs = Math . max ( currentPostpone , untilTimeMs ) ; } context . telemetry . properties . postpone = String ( untilTimeMs - Date . now ( ) ) ; await ext . context . globalState . update ( stateKeys . surveyPostponedUntilTime , untilTimeMs ) ; } async function getIsSurveyAccessible ( ) { try { const response = await httpGet ( linkToSurvey ) ; return ! ! response ; } catch ( err ) { return false ; } } function getShouldNeverShowSurvey ( ) { return ext . context . globalState . get < boolean > ( stateKeys . neverShowSurvey , false ) ; } function getIsSurveyPostponed ( ) { const postponedUntilTime = ext . context . globalState . get < number > ( stateKeys . surveyPostponedUntilTime , 0 ) ; return postponedUntilTime > Date . now ( ) ; } function getSessionLengthMs ( context ) { if ( usageSessionStart === undefined ) { usageSessionStart = Date . now ( ) ; return 0 ; } else { return Date . now ( ) - usageSessionStart ; } }