@@ -3,7 +3,6 @@ import { Disposable, EventEmitter } from 'vscode';
3
3
import { Commands } from '../constants.commands' ;
4
4
import type { TrackedUsageKeys } from '../constants.telemetry' ;
5
5
import type { Container } from '../container' ;
6
- import { entries } from '../system/object' ;
7
6
import { wait } from '../system/promise' ;
8
7
import { setContext } from '../system/vscode/context' ;
9
8
import type { UsageChangeEvent } from './usageTracker' ;
@@ -16,66 +15,108 @@ export enum WalkthroughContextKeys {
16
15
Integrations = 'integrations' ,
17
16
}
18
17
18
+ type WalkthroughUsage = { states : TrackedUsageKeys [ ] ; usage : TrackedUsageKeys [ ] } ;
19
+
20
+ const walkthroughRequiredMapping : Readonly < Map < WalkthroughContextKeys , WalkthroughUsage > > = new Map <
21
+ WalkthroughContextKeys ,
22
+ WalkthroughUsage
23
+ > ( [
24
+ [
25
+ WalkthroughContextKeys . GettingStarted ,
26
+ {
27
+ states : [
28
+ `command:${ Commands . PlusStartPreviewTrial } :executed` ,
29
+ `command:${ Commands . PlusReactivateProTrial } :executed` ,
30
+ `command:${ Commands . OpenWalkthrough } :executed` ,
31
+ `command:${ Commands . GetStarted } :executed` ,
32
+ ] ,
33
+ usage : [ ] ,
34
+ } ,
35
+ ] ,
36
+ [
37
+ WalkthroughContextKeys . VisualizeCodeHistory ,
38
+ {
39
+ states : [
40
+ `command:${ Commands . PlusStartPreviewTrial } :executed` ,
41
+ `command:${ Commands . PlusReactivateProTrial } :executed` ,
42
+ ] ,
43
+ usage : [
44
+ 'graphDetailsView:shown' ,
45
+ 'graphView:shown' ,
46
+ 'graphWebview:shown' ,
47
+ 'commitDetailsView:shown' ,
48
+ `command:${ Commands . ShowGraph } :executed` ,
49
+ `command:${ Commands . ShowGraphPage } :executed` ,
50
+ `command:${ Commands . ShowGraphView } :executed` ,
51
+ `command:${ Commands . ShowInCommitGraph } :executed` ,
52
+ `command:${ Commands . ShowInCommitGraphView } :executed` ,
53
+ ] ,
54
+ } ,
55
+ ] ,
56
+ [
57
+ WalkthroughContextKeys . PrReviews ,
58
+ {
59
+ states : [
60
+ `command:${ Commands . PlusStartPreviewTrial } :executed` ,
61
+ `command:${ Commands . PlusReactivateProTrial } :executed` ,
62
+ ] ,
63
+ usage : [
64
+ 'launchpadView:shown' ,
65
+ 'worktreesView:shown' ,
66
+ `command:${ Commands . ShowLaunchpad } :executed` ,
67
+ `command:${ Commands . ShowLaunchpadView } :executed` ,
68
+ `command:${ Commands . GitCommandsWorktree } :executed` ,
69
+ `command:${ Commands . GitCommandsWorktreeCreate } :executed` ,
70
+ `command:${ Commands . GitCommandsWorktreeDelete } :executed` ,
71
+ `command:${ Commands . GitCommandsWorktreeOpen } :executed` ,
72
+ ] ,
73
+ } ,
74
+ ] ,
75
+ [
76
+ WalkthroughContextKeys . StreamlineCollaboration ,
77
+ {
78
+ states : [
79
+ `command:${ Commands . PlusStartPreviewTrial } :executed` ,
80
+ `command:${ Commands . PlusReactivateProTrial } :executed` ,
81
+ ] ,
82
+ usage : [ `command:${ Commands . CreateCloudPatch } :executed` , `command:${ Commands . CreatePatch } :executed` ] ,
83
+ } ,
84
+ ] ,
85
+ [
86
+ WalkthroughContextKeys . Integrations ,
87
+ {
88
+ states : [ ] ,
89
+ usage : [
90
+ `command:${ Commands . PlusConnectCloudIntegrations } :executed` ,
91
+ `command:${ Commands . PlusManageCloudIntegrations } :executed` ,
92
+ ] ,
93
+ } ,
94
+ ] ,
95
+ ] ) ;
96
+
19
97
export class WalkthroughStateProvider implements Disposable {
20
98
protected disposables : Disposable [ ] = [ ] ;
21
- private readonly state = new Map < WalkthroughContextKeys , boolean > ( ) ;
99
+ private readonly completed = new Set < WalkthroughContextKeys > ( ) ;
22
100
readonly walkthroughSize : number ;
23
- private isInitialized = false ;
24
- private _initPromise : Promise < void > | undefined ;
25
101
26
- /**
27
- * using reversed map (instead of direct map as walkthroughToTracking Record<WalkthroughContextKeys, TrackedUsageKeys[]>)
28
- * makes code less readable, but prevents duplicated usageTracker keys
29
- */
30
- private readonly walkthroughByTracking : Partial < Record < TrackedUsageKeys , WalkthroughContextKeys > > = {
31
- [ `command:${ Commands . PlusStartPreviewTrial } :executed` ] : WalkthroughContextKeys . GettingStarted ,
32
- [ `command:${ Commands . PlusReactivateProTrial } :executed` ] : WalkthroughContextKeys . GettingStarted ,
33
- [ `command:${ Commands . OpenWalkthrough } :executed` ] : WalkthroughContextKeys . GettingStarted ,
34
- [ `command:${ Commands . GetStarted } :executed` ] : WalkthroughContextKeys . GettingStarted ,
35
-
36
- 'graphDetailsView:shown' : WalkthroughContextKeys . VisualizeCodeHistory ,
37
- 'graphView:shown' : WalkthroughContextKeys . VisualizeCodeHistory ,
38
- 'graphWebview:shown' : WalkthroughContextKeys . VisualizeCodeHistory ,
39
- 'commitDetailsView:shown' : WalkthroughContextKeys . VisualizeCodeHistory ,
40
- [ `command:${ Commands . ShowGraph } :executed` ] : WalkthroughContextKeys . VisualizeCodeHistory ,
41
- [ `command:${ Commands . ShowGraphPage } :executed` ] : WalkthroughContextKeys . VisualizeCodeHistory ,
42
- [ `command:${ Commands . ShowGraphView } :executed` ] : WalkthroughContextKeys . VisualizeCodeHistory ,
43
- [ `command:${ Commands . ShowInCommitGraph } :executed` ] : WalkthroughContextKeys . VisualizeCodeHistory ,
44
- [ `command:${ Commands . ShowInCommitGraphView } :executed` ] : WalkthroughContextKeys . VisualizeCodeHistory ,
45
-
46
- 'launchpadView:shown' : WalkthroughContextKeys . PrReviews ,
47
- 'worktreesView:shown' : WalkthroughContextKeys . PrReviews ,
48
- [ `command:${ Commands . ShowLaunchpad } :executed` ] : WalkthroughContextKeys . PrReviews ,
49
- [ `command:${ Commands . ShowLaunchpadView } :executed` ] : WalkthroughContextKeys . PrReviews ,
50
- [ `command:${ Commands . GitCommandsWorktree } :executed` ] : WalkthroughContextKeys . PrReviews ,
51
- [ `command:${ Commands . GitCommandsWorktreeCreate } :executed` ] : WalkthroughContextKeys . PrReviews ,
52
- [ `command:${ Commands . GitCommandsWorktreeDelete } :executed` ] : WalkthroughContextKeys . PrReviews ,
53
- [ `command:${ Commands . GitCommandsWorktreeOpen } :executed` ] : WalkthroughContextKeys . PrReviews ,
54
-
55
- [ `command:${ Commands . CreateCloudPatch } :executed` ] : WalkthroughContextKeys . StreamlineCollaboration ,
56
- [ `command:${ Commands . CreatePatch } :executed` ] : WalkthroughContextKeys . StreamlineCollaboration ,
57
-
58
- [ `command:${ Commands . PlusConnectCloudIntegrations } :executed` ] : WalkthroughContextKeys . Integrations ,
59
- [ `command:${ Commands . PlusManageCloudIntegrations } :executed` ] : WalkthroughContextKeys . Integrations ,
60
- } ;
61
102
private readonly _onProgressChanged = new EventEmitter < void > ( ) ;
103
+ get onProgressChanged ( ) : Event < void > {
104
+ return this . _onProgressChanged . event ;
105
+ }
62
106
63
107
constructor ( private readonly container : Container ) {
64
108
this . disposables . push ( this . container . usage . onDidChange ( this . onUsageChanged , this ) ) ;
65
- this . walkthroughSize = Object . values ( WalkthroughContextKeys ) . length ;
109
+ this . walkthroughSize = walkthroughRequiredMapping . size ;
66
110
67
111
this . initializeState ( ) ;
68
112
}
69
113
70
114
private initializeState ( ) {
71
- for ( const key of Object . values ( WalkthroughContextKeys ) ) {
72
- this . state . set ( key , false ) ;
73
- }
74
- entries ( this . walkthroughByTracking ) . forEach ( ( [ usageKey , walkthroughKey ] ) => {
75
- if ( ! this . state . get ( walkthroughKey ) && this . container . usage . isUsed ( usageKey ) ) {
76
- void this . completeStep ( walkthroughKey ) ;
115
+ for ( const key of walkthroughRequiredMapping . keys ( ) ) {
116
+ if ( this . validateStep ( key ) ) {
117
+ void this . completeStep ( key ) ;
77
118
}
78
- } ) ;
119
+ }
79
120
this . _onProgressChanged . fire ( undefined ) ;
80
121
}
81
122
@@ -84,25 +125,42 @@ export class WalkthroughStateProvider implements Disposable {
84
125
if ( ! usageTrackingKey ) {
85
126
return ;
86
127
}
87
- const walkthroughKey = this . walkthroughByTracking [ usageTrackingKey ] ;
88
- if ( walkthroughKey ) {
89
- void this . completeStep ( walkthroughKey ) ;
128
+
129
+ const stepsToValidate = this . getStepsFromUsage ( usageTrackingKey ) ;
130
+ let shouldFire = false ;
131
+ for ( const step of stepsToValidate ) {
132
+ // no need to check if the step is already completed
133
+ if ( this . completed . has ( step ) ) {
134
+ continue ;
135
+ }
136
+
137
+ if ( this . validateStep ( step ) ) {
138
+ void this . completeStep ( step ) ;
139
+ this . container . telemetry . sendEvent ( 'walkthrough/completion' , {
140
+ 'context.key' : step ,
141
+ } ) ;
142
+ shouldFire = true ;
143
+ }
144
+ }
145
+ if ( shouldFire ) {
90
146
this . _onProgressChanged . fire ( undefined ) ;
91
147
}
92
148
}
93
149
150
+ private _isInitialized : boolean = false ;
151
+ private _initPromise : Promise < void > | undefined ;
94
152
/**
95
153
* Walkthrough view is not ready to listen to context changes immediately after opening VSCode with the walkthrough page opened
96
154
* As we're not able to check if the walkthrough is ready, we need to add a delay.
97
155
* The 1s delay will not be too annoying for user but it's enough to init
98
156
*/
99
157
private async waitForWalkthroughInitialized ( ) {
100
- if ( this . isInitialized ) {
158
+ if ( this . _isInitialized ) {
101
159
return ;
102
160
}
103
161
if ( ! this . _initPromise ) {
104
162
this . _initPromise = wait ( 1000 ) . then ( ( ) => {
105
- this . isInitialized = true ;
163
+ this . _isInitialized = true ;
106
164
} ) ;
107
165
}
108
166
await this . _initPromise ;
@@ -114,17 +172,13 @@ export class WalkthroughStateProvider implements Disposable {
114
172
* we don't have an ability to reset the flag
115
173
*/
116
174
private async completeStep ( key : WalkthroughContextKeys ) {
117
- this . state . set ( key , true ) ;
175
+ this . completed . add ( key ) ;
118
176
await this . waitForWalkthroughInitialized ( ) ;
119
177
void setContext ( `gitlens:walkthroughState:${ key } ` , true ) ;
120
178
}
121
179
122
- get onProgressChanged ( ) : Event < void > {
123
- return this . _onProgressChanged . event ;
124
- }
125
-
126
180
get doneCount ( ) {
127
- return [ ... this . state . values ( ) ] . filter ( x => x ) . length ;
181
+ return this . completed . size ;
128
182
}
129
183
130
184
get progress ( ) {
@@ -134,4 +188,26 @@ export class WalkthroughStateProvider implements Disposable {
134
188
dispose ( ) : void {
135
189
Disposable . from ( ...this . disposables ) . dispose ( ) ;
136
190
}
191
+
192
+ private getStepsFromUsage ( usageKey : TrackedUsageKeys ) : WalkthroughContextKeys [ ] {
193
+ const keys : WalkthroughContextKeys [ ] = [ ] ;
194
+ for ( const [ key , { states, usage : events } ] of walkthroughRequiredMapping ) {
195
+ if ( states . includes ( usageKey ) || events . includes ( usageKey ) ) {
196
+ keys . push ( key ) ;
197
+ }
198
+ }
199
+
200
+ return keys ;
201
+ }
202
+
203
+ 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 ) ) ) {
206
+ return false ;
207
+ }
208
+ if ( events . length && ! events . some ( event => this . container . usage . isUsed ( event ) ) ) {
209
+ return false ;
210
+ }
211
+ return true ;
212
+ }
137
213
}
0 commit comments