@@ -7,74 +7,59 @@ import fetch, { Response } from 'node-fetch';
7
7
import * as vscode from 'vscode' ;
8
8
import { load } from 'js-yaml' ;
9
9
import { CacheHelper } from './util/cache' ;
10
+ import { Disposable , disposeAll } from './util/dispose' ;
10
11
11
- export const RELEASE_NOTES_LAST_READ_KEY = 'gitpod.lastReadReleaseNotesId' ;
12
-
13
- export function registerReleaseNotesView ( context : vscode . ExtensionContext ) {
14
- const cacheHelper = new CacheHelper ( context ) ;
12
+ export class ReleaseNotes extends Disposable {
13
+ public static readonly viewType = 'gitpodReleaseNotes' ;
14
+ public static readonly websiteHost = 'https://www.gitpod.io' ;
15
+ public static readonly RELEASE_NOTES_LAST_READ_KEY = 'gitpod.lastReadReleaseNotesId' ;
15
16
16
- async function shouldShowReleaseNotes ( lastReadId : string | undefined ) {
17
- const releaseId = await getLastPublish ( cacheHelper ) ;
18
- console . log ( `gitpod release notes lastReadId: ${ lastReadId } , latestReleaseId: ${ releaseId } ` ) ;
19
- return releaseId !== lastReadId ;
20
- }
17
+ private panel : vscode . WebviewPanel | undefined ;
18
+ private panelDisposables : vscode . Disposable [ ] = [ ] ;
19
+ private lastReadId : string | undefined ;
20
+ private cacheHelper = new CacheHelper ( this . context ) ;
21
21
22
- context . subscriptions . push (
23
- vscode . commands . registerCommand ( 'gitpod.showReleaseNotes' , ( ) => {
24
- ReleaseNotesPanel . createOrShow ( context , cacheHelper ) ;
25
- } )
26
- ) ;
22
+ constructor (
23
+ private readonly context : vscode . ExtensionContext ,
24
+ ) {
25
+ super ( ) ;
27
26
28
- // sync between machines
29
- context . globalState . setKeysForSync ( [ RELEASE_NOTES_LAST_READ_KEY ] ) ;
27
+ this . lastReadId = this . context . globalState . get < string > ( ReleaseNotes . RELEASE_NOTES_LAST_READ_KEY ) ;
30
28
31
- const lastReadId = context . globalState . get < string > ( RELEASE_NOTES_LAST_READ_KEY ) ;
32
- shouldShowReleaseNotes ( lastReadId ) . then ( shouldShow => {
33
- if ( shouldShow ) {
34
- ReleaseNotesPanel . createOrShow ( context , cacheHelper ) ;
35
- }
36
- } ) ;
37
- }
29
+ this . _register ( vscode . commands . registerCommand ( 'gitpod.showReleaseNotes' , ( ) => this . createOrShow ( ) ) ) ;
38
30
39
- function getResponseCacheTime ( resp : Response ) {
40
- const v = resp . headers . get ( 'Cache-Control' ) ;
41
- if ( ! v ) {
42
- return undefined ;
31
+ this . showIfNewRelease ( this . lastReadId ) ;
43
32
}
44
- const t = / m a x - a g e = ( \d + ) / . exec ( v ) ;
45
- if ( ! t ) {
46
- return undefined ;
33
+
34
+ private async getLastPublish ( ) {
35
+ const url = `${ ReleaseNotes . websiteHost } /changelog/latest` ;
36
+ return this . cacheHelper . getOrRefresh ( url , async ( ) => {
37
+ const resp = await fetch ( url ) ;
38
+ if ( ! resp . ok ) {
39
+ throw new Error ( `Getting latest releaseId failed: ${ resp . statusText } ` ) ;
40
+ }
41
+ const { releaseId } = JSON . parse ( await resp . text ( ) ) ;
42
+ return {
43
+ value : releaseId as string ,
44
+ ttl : this . getResponseCacheTime ( resp ) ,
45
+ } ;
46
+ } ) ;
47
47
}
48
- return Number ( t [ 1 ] ) ;
49
- }
50
48
51
- async function getLastPublish ( cacheHelper : CacheHelper ) {
52
- const url = `${ websiteHost } /changelog/latest` ;
53
- return cacheHelper . getOrRefresh ( url , async ( ) => {
54
- const resp = await fetch ( url ) ;
55
- if ( ! resp . ok ) {
56
- throw new Error ( `Getting latest releaseId failed: ${ resp . statusText } ` ) ;
49
+ private getResponseCacheTime ( resp : Response ) {
50
+ const cacheControlHeader = resp . headers . get ( 'Cache-Control' ) ;
51
+ if ( ! cacheControlHeader ) {
52
+ return undefined ;
57
53
}
58
- const { releaseId } = JSON . parse ( await resp . text ( ) ) ;
59
- return {
60
- value : releaseId as string ,
61
- ttl : getResponseCacheTime ( resp ) ,
62
- } ;
63
- } ) ;
64
-
65
- }
66
-
67
- const websiteHost = 'https://www.gitpod.io' ;
68
-
69
- class ReleaseNotesPanel {
70
- public static currentPanel : ReleaseNotesPanel | undefined ;
71
- public static readonly viewType = 'gitpodReleaseNotes' ;
72
- private readonly panel : vscode . WebviewPanel ;
73
- private lastReadId : string | undefined ;
74
- private _disposables : vscode . Disposable [ ] = [ ] ;
54
+ const match = / m a x - a g e = ( \d + ) / . exec ( cacheControlHeader ) ;
55
+ if ( ! match ) {
56
+ return undefined ;
57
+ }
58
+ return parseInt ( match [ 1 ] , 10 ) ;
59
+ }
75
60
76
61
private async loadChangelog ( releaseId : string ) {
77
- const url = `${ websiteHost } /changelog/raw-markdown?releaseId=${ releaseId } ` ;
62
+ const url = `${ ReleaseNotes . websiteHost } /changelog/raw-markdown?releaseId=${ releaseId } ` ;
78
63
const md = await this . cacheHelper . getOrRefresh ( url , async ( ) => {
79
64
const resp = await fetch ( url ) ;
80
65
if ( ! resp . ok ) {
@@ -83,7 +68,7 @@ class ReleaseNotesPanel {
83
68
const md = await resp . text ( ) ;
84
69
return {
85
70
value : md ,
86
- ttl : getResponseCacheTime ( resp ) ,
71
+ ttl : this . getResponseCacheTime ( resp ) ,
87
72
} ;
88
73
} ) ;
89
74
@@ -122,12 +107,14 @@ class ReleaseNotesPanel {
122
107
] . join ( '\n\n' ) ;
123
108
}
124
109
125
- public async updateHtml ( releaseId ?: string ) {
126
- if ( ! releaseId ) {
127
- releaseId = await getLastPublish ( this . cacheHelper ) ;
110
+ public async updateHtml ( ) {
111
+ if ( ! this . panel ?. visible ) {
112
+ return ;
128
113
}
114
+
115
+ const releaseId = await this . getLastPublish ( ) ;
129
116
const mdContent = await this . loadChangelog ( releaseId ) ;
130
- const html = await vscode . commands . executeCommand ( 'markdown.api.render' , mdContent ) as string ;
117
+ const html = await vscode . commands . executeCommand < string > ( 'markdown.api.render' , mdContent ) ;
131
118
this . panel . webview . html = `<!DOCTYPE html>
132
119
<html lang="en">
133
120
<head>
@@ -143,63 +130,49 @@ class ReleaseNotesPanel {
143
130
${ html }
144
131
</body>
145
132
</html>` ;
146
- if ( ! this . lastReadId || releaseId > this . lastReadId ) {
147
- await this . context . globalState . update ( RELEASE_NOTES_LAST_READ_KEY , releaseId ) ;
133
+ if ( releaseId !== this . lastReadId ) {
134
+ await this . context . globalState . update ( ReleaseNotes . RELEASE_NOTES_LAST_READ_KEY , releaseId ) ;
148
135
this . lastReadId = releaseId ;
149
136
}
150
137
}
151
138
152
- public static createOrShow ( context : vscode . ExtensionContext , cacheHelper : CacheHelper ) {
153
- if ( ReleaseNotesPanel . currentPanel ) {
154
- ReleaseNotesPanel . currentPanel . panel . reveal ( ) ;
139
+ private async showIfNewRelease ( lastReadId : string | undefined ) {
140
+ const releaseId = await this . getLastPublish ( ) ;
141
+ console . log ( `gitpod release notes lastReadId: ${ lastReadId } , latestReleaseId: ${ releaseId } ` ) ;
142
+ if ( releaseId !== lastReadId ) {
143
+ this . createOrShow ( ) ;
144
+ }
145
+ }
146
+
147
+ public createOrShow ( ) {
148
+ if ( this . panel ) {
149
+ this . panel . reveal ( ) ;
155
150
return ;
156
151
}
157
152
158
- const panel = vscode . window . createWebviewPanel (
159
- ReleaseNotesPanel . viewType ,
153
+ this . panel = vscode . window . createWebviewPanel (
154
+ ReleaseNotes . viewType ,
160
155
'Gitpod Release Notes' ,
161
156
vscode . ViewColumn . Beside ,
162
157
{ enableScripts : true } ,
163
158
) ;
164
-
165
- ReleaseNotesPanel . currentPanel = new ReleaseNotesPanel ( context , cacheHelper , panel ) ;
166
- }
167
-
168
- public static revive ( context : vscode . ExtensionContext , cacheHelper : CacheHelper , panel : vscode . WebviewPanel ) {
169
- ReleaseNotesPanel . currentPanel = new ReleaseNotesPanel ( context , cacheHelper , panel ) ;
170
- }
171
-
172
- private constructor (
173
- private readonly context : vscode . ExtensionContext ,
174
- private readonly cacheHelper : CacheHelper ,
175
- panel : vscode . WebviewPanel
176
- ) {
177
- this . lastReadId = this . context . globalState . get < string > ( RELEASE_NOTES_LAST_READ_KEY ) ;
178
- this . panel = panel ;
179
-
180
- this . updateHtml ( ) ;
181
-
182
- this . panel . onDidDispose ( ( ) => this . dispose ( ) , null , this . _disposables ) ;
159
+ this . panel . onDidDispose ( ( ) => {
160
+ disposeAll ( this . panelDisposables ) ;
161
+ this . panel = undefined ;
162
+ this . panelDisposables = [ ] ;
163
+ } , null , this . panelDisposables ) ;
183
164
this . panel . onDidChangeViewState (
184
- ( ) => {
185
- if ( this . panel . visible ) {
186
- this . updateHtml ( ) ;
187
- }
188
- } ,
165
+ ( ) => this . updateHtml ( ) ,
189
166
null ,
190
- this . _disposables
167
+ this . panelDisposables
191
168
) ;
169
+ this . updateHtml ( ) ;
192
170
}
193
171
194
- public dispose ( ) {
195
- ReleaseNotesPanel . currentPanel = undefined ;
196
- this . panel . dispose ( ) ;
197
- while ( this . _disposables . length ) {
198
- const x = this . _disposables . pop ( ) ;
199
- if ( x ) {
200
- x . dispose ( ) ;
201
- }
202
- }
172
+ override dispose ( ) {
173
+ super . dispose ( ) ;
174
+ disposeAll ( this . panelDisposables ) ;
175
+ this . panel ?. dispose ( ) ;
203
176
}
204
177
}
205
178
0 commit comments