@@ -7,74 +7,59 @@ import fetch, { Response } from 'node-fetch';
77import * as vscode from 'vscode' ;
88import { load } from 'js-yaml' ;
99import { CacheHelper } from './util/cache' ;
10+ import { Disposable , disposeAll } from './util/dispose' ;
1011
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' ;
1516
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 ) ;
2121
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 ( ) ;
2726
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 ) ;
3028
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 ( ) ) ) ;
3830
39- function getResponseCacheTime ( resp : Response ) {
40- const v = resp . headers . get ( 'Cache-Control' ) ;
41- if ( ! v ) {
42- return undefined ;
31+ this . showIfNewRelease ( this . lastReadId ) ;
4332 }
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+ } ) ;
4747 }
48- return Number ( t [ 1 ] ) ;
49- }
5048
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 ;
5753 }
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+ }
7560
7661 private async loadChangelog ( releaseId : string ) {
77- const url = `${ websiteHost } /changelog/raw-markdown?releaseId=${ releaseId } ` ;
62+ const url = `${ ReleaseNotes . websiteHost } /changelog/raw-markdown?releaseId=${ releaseId } ` ;
7863 const md = await this . cacheHelper . getOrRefresh ( url , async ( ) => {
7964 const resp = await fetch ( url ) ;
8065 if ( ! resp . ok ) {
@@ -83,7 +68,7 @@ class ReleaseNotesPanel {
8368 const md = await resp . text ( ) ;
8469 return {
8570 value : md ,
86- ttl : getResponseCacheTime ( resp ) ,
71+ ttl : this . getResponseCacheTime ( resp ) ,
8772 } ;
8873 } ) ;
8974
@@ -122,12 +107,14 @@ class ReleaseNotesPanel {
122107 ] . join ( '\n\n' ) ;
123108 }
124109
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 ;
128113 }
114+
115+ const releaseId = await this . getLastPublish ( ) ;
129116 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 ) ;
131118 this . panel . webview . html = `<!DOCTYPE html>
132119<html lang="en">
133120<head>
@@ -143,63 +130,49 @@ class ReleaseNotesPanel {
143130 ${ html }
144131 </body>
145132</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 ) ;
148135 this . lastReadId = releaseId ;
149136 }
150137 }
151138
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 ( ) ;
155150 return ;
156151 }
157152
158- const panel = vscode . window . createWebviewPanel (
159- ReleaseNotesPanel . viewType ,
153+ this . panel = vscode . window . createWebviewPanel (
154+ ReleaseNotes . viewType ,
160155 'Gitpod Release Notes' ,
161156 vscode . ViewColumn . Beside ,
162157 { enableScripts : true } ,
163158 ) ;
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 ) ;
183164 this . panel . onDidChangeViewState (
184- ( ) => {
185- if ( this . panel . visible ) {
186- this . updateHtml ( ) ;
187- }
188- } ,
165+ ( ) => this . updateHtml ( ) ,
189166 null ,
190- this . _disposables
167+ this . panelDisposables
191168 ) ;
169+ this . updateHtml ( ) ;
192170 }
193171
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 ( ) ;
203176 }
204177}
205178
0 commit comments