1
1
import type { Event } from 'vscode' ;
2
2
import { Disposable , EventEmitter } from 'vscode' ;
3
3
import { Commands } from '../constants.commands' ;
4
+ import { SubscriptionState } from '../constants.subscription' ;
4
5
import type { TrackedUsageKeys } from '../constants.telemetry' ;
5
6
import type { Container } from '../container' ;
7
+ import type { SubscriptionChangeEvent } from '../plus/gk/account/subscriptionService' ;
6
8
import { wait } from '../system/promise' ;
7
9
import { setContext } from '../system/vscode/context' ;
8
10
import type { UsageChangeEvent } from './usageTracker' ;
@@ -15,7 +17,23 @@ export enum WalkthroughContextKeys {
15
17
Integrations = 'integrations' ,
16
18
}
17
19
18
- type WalkthroughUsage = { states : TrackedUsageKeys [ ] ; usage : TrackedUsageKeys [ ] } ;
20
+ type WalkthroughUsage = {
21
+ subscriptionStates ?: SubscriptionState [ ] | Readonly < SubscriptionState [ ] > ;
22
+ subscriptionCommands ?: TrackedUsageKeys [ ] | Readonly < TrackedUsageKeys [ ] > ;
23
+ usage : TrackedUsageKeys [ ] ;
24
+ } ;
25
+
26
+ const triedProStates : Readonly < SubscriptionState [ ] > = [
27
+ SubscriptionState . ProTrial ,
28
+ SubscriptionState . ProTrialExpired ,
29
+ SubscriptionState . ProTrialReactivationEligible ,
30
+ SubscriptionState . Paid ,
31
+ ] ;
32
+
33
+ const tryProCommands : Readonly < TrackedUsageKeys [ ] > = [
34
+ `command:${ Commands . PlusStartPreviewTrial } :executed` ,
35
+ `command:${ Commands . PlusReactivateProTrial } :executed` ,
36
+ ] ;
19
37
20
38
const walkthroughRequiredMapping : Readonly < Map < WalkthroughContextKeys , WalkthroughUsage > > = new Map <
21
39
WalkthroughContextKeys ,
@@ -24,22 +42,16 @@ const walkthroughRequiredMapping: Readonly<Map<WalkthroughContextKeys, Walkthrou
24
42
[
25
43
WalkthroughContextKeys . GettingStarted ,
26
44
{
27
- states : [
28
- `command:${ Commands . PlusStartPreviewTrial } :executed` ,
29
- `command:${ Commands . PlusReactivateProTrial } :executed` ,
30
- `command:${ Commands . OpenWalkthrough } :executed` ,
31
- `command:${ Commands . GetStarted } :executed` ,
32
- ] ,
45
+ subscriptionStates : triedProStates ,
46
+ subscriptionCommands : tryProCommands ,
33
47
usage : [ ] ,
34
48
} ,
35
49
] ,
36
50
[
37
51
WalkthroughContextKeys . VisualizeCodeHistory ,
38
52
{
39
- states : [
40
- `command:${ Commands . PlusStartPreviewTrial } :executed` ,
41
- `command:${ Commands . PlusReactivateProTrial } :executed` ,
42
- ] ,
53
+ subscriptionStates : triedProStates ,
54
+ subscriptionCommands : tryProCommands ,
43
55
usage : [
44
56
'graphDetailsView:shown' ,
45
57
'graphView:shown' ,
@@ -56,10 +68,8 @@ const walkthroughRequiredMapping: Readonly<Map<WalkthroughContextKeys, Walkthrou
56
68
[
57
69
WalkthroughContextKeys . PrReviews ,
58
70
{
59
- states : [
60
- `command:${ Commands . PlusStartPreviewTrial } :executed` ,
61
- `command:${ Commands . PlusReactivateProTrial } :executed` ,
62
- ] ,
71
+ subscriptionStates : triedProStates ,
72
+ subscriptionCommands : tryProCommands ,
63
73
usage : [
64
74
'launchpadView:shown' ,
65
75
'worktreesView:shown' ,
@@ -75,17 +85,14 @@ const walkthroughRequiredMapping: Readonly<Map<WalkthroughContextKeys, Walkthrou
75
85
[
76
86
WalkthroughContextKeys . StreamlineCollaboration ,
77
87
{
78
- states : [
79
- `command:${ Commands . PlusStartPreviewTrial } :executed` ,
80
- `command:${ Commands . PlusReactivateProTrial } :executed` ,
81
- ] ,
88
+ subscriptionStates : triedProStates ,
89
+ subscriptionCommands : tryProCommands ,
82
90
usage : [ `command:${ Commands . CreateCloudPatch } :executed` , `command:${ Commands . CreatePatch } :executed` ] ,
83
91
} ,
84
92
] ,
85
93
[
86
94
WalkthroughContextKeys . Integrations ,
87
95
{
88
- states : [ ] ,
89
96
usage : [
90
97
`command:${ Commands . PlusConnectCloudIntegrations } :executed` ,
91
98
`command:${ Commands . PlusManageCloudIntegrations } :executed` ,
@@ -95,23 +102,28 @@ const walkthroughRequiredMapping: Readonly<Map<WalkthroughContextKeys, Walkthrou
95
102
] ) ;
96
103
97
104
export class WalkthroughStateProvider implements Disposable {
105
+ readonly walkthroughSize = walkthroughRequiredMapping . size ;
98
106
protected disposables : Disposable [ ] = [ ] ;
99
107
private readonly completed = new Set < WalkthroughContextKeys > ( ) ;
100
- readonly walkthroughSize : number ;
108
+ private subscriptionState : SubscriptionState | undefined ;
101
109
102
110
private readonly _onProgressChanged = new EventEmitter < void > ( ) ;
103
111
get onProgressChanged ( ) : Event < void > {
104
112
return this . _onProgressChanged . event ;
105
113
}
106
114
107
115
constructor ( private readonly container : Container ) {
108
- this . disposables . push ( this . container . usage . onDidChange ( this . onUsageChanged , this ) ) ;
109
- this . walkthroughSize = walkthroughRequiredMapping . size ;
116
+ this . disposables . push (
117
+ this . container . usage . onDidChange ( this . onUsageChanged , this ) ,
118
+ this . container . subscription . onDidChange ( this . onSubscriptionChanged , this ) ,
119
+ ) ;
110
120
111
- this . initializeState ( ) ;
121
+ void this . initializeState ( ) ;
112
122
}
113
123
114
- private initializeState ( ) {
124
+ private async initializeState ( ) {
125
+ this . subscriptionState = ( await this . container . subscription . getSubscription ( true ) ) . state ;
126
+
115
127
for ( const key of walkthroughRequiredMapping . keys ( ) ) {
116
128
if ( this . validateStep ( key ) ) {
117
129
void this . completeStep ( key ) ;
@@ -147,6 +159,29 @@ export class WalkthroughStateProvider implements Disposable {
147
159
}
148
160
}
149
161
162
+ private onSubscriptionChanged ( e : SubscriptionChangeEvent ) {
163
+ this . subscriptionState = e . current . state ;
164
+ const stepsToValidate = this . getStepsFromSubscriptionState ( e . current . state ) ;
165
+ let shouldFire = false ;
166
+ for ( const step of stepsToValidate ) {
167
+ // no need to check if the step is already completed
168
+ if ( this . completed . has ( step ) ) {
169
+ continue ;
170
+ }
171
+
172
+ if ( this . validateStep ( step ) ) {
173
+ void this . completeStep ( step ) ;
174
+ this . container . telemetry . sendEvent ( 'walkthrough/completion' , {
175
+ 'context.key' : step ,
176
+ } ) ;
177
+ shouldFire = true ;
178
+ }
179
+ }
180
+ if ( shouldFire ) {
181
+ this . _onProgressChanged . fire ( undefined ) ;
182
+ }
183
+ }
184
+
150
185
private _isInitialized : boolean = false ;
151
186
private _initPromise : Promise < void > | undefined ;
152
187
/**
@@ -191,8 +226,19 @@ export class WalkthroughStateProvider implements Disposable {
191
226
192
227
private getStepsFromUsage ( usageKey : TrackedUsageKeys ) : WalkthroughContextKeys [ ] {
193
228
const keys : WalkthroughContextKeys [ ] = [ ] ;
194
- for ( const [ key , { states, usage : events } ] of walkthroughRequiredMapping ) {
195
- if ( states . includes ( usageKey ) || events . includes ( usageKey ) ) {
229
+ for ( const [ key , { subscriptionCommands, usage } ] of walkthroughRequiredMapping ) {
230
+ if ( subscriptionCommands ?. includes ( usageKey ) || usage . includes ( usageKey ) ) {
231
+ keys . push ( key ) ;
232
+ }
233
+ }
234
+
235
+ return keys ;
236
+ }
237
+
238
+ private getStepsFromSubscriptionState ( _state : SubscriptionState ) : WalkthroughContextKeys [ ] {
239
+ const keys : WalkthroughContextKeys [ ] = [ ] ;
240
+ for ( const [ key , { subscriptionStates } ] of walkthroughRequiredMapping ) {
241
+ if ( subscriptionStates != null ) {
196
242
keys . push ( key ) ;
197
243
}
198
244
}
@@ -201,11 +247,24 @@ export class WalkthroughStateProvider implements Disposable {
201
247
}
202
248
203
249
private validateStep ( key : WalkthroughContextKeys ) : boolean {
204
- const { states, usage : events } = walkthroughRequiredMapping . get ( key ) ! ;
205
- if ( states . length && ! states . some ( state => this . container . usage . isUsed ( state ) ) ) {
250
+ const { subscriptionStates, subscriptionCommands, usage } = walkthroughRequiredMapping . get ( key ) ! ;
251
+
252
+ let subscriptionState : boolean | undefined ;
253
+ if ( subscriptionStates != null && subscriptionStates . length > 0 ) {
254
+ subscriptionState = this . subscriptionState != null && subscriptionStates . includes ( this . subscriptionState ) ;
255
+ }
256
+ let subscriptionCommandState : boolean | undefined ;
257
+ if ( subscriptionCommands != null && subscriptionCommands . length > 0 ) {
258
+ subscriptionCommandState = subscriptionCommands . some ( event => this . container . usage . isUsed ( event ) ) ;
259
+ }
260
+ if (
261
+ ( subscriptionState === undefined && subscriptionCommandState === false ) ||
262
+ ( subscriptionState === false && subscriptionCommandState !== true )
263
+ ) {
206
264
return false ;
207
265
}
208
- if ( events . length && ! events . some ( event => this . container . usage . isUsed ( event ) ) ) {
266
+
267
+ if ( usage . length > 0 && ! usage . some ( event => this . container . usage . isUsed ( event ) ) ) {
209
268
return false ;
210
269
}
211
270
return true ;
0 commit comments