11/* eslint-disable @typescript-eslint/naming-convention */
22import * as vscode from 'vscode' ;
33import * as path from 'path' ;
4- import { Peer , PeerGroup } from './types' ;
4+ import { Peer , PeerGroup , PeersResponse } from './types' ;
55import { Utils } from 'vscode-uri' ;
66import { Tailscale } from './tailscale/cli' ;
77import { ConfigManager } from './config-manager' ;
@@ -34,6 +34,8 @@ export class NodeExplorerProvider
3434 // We want to use an array as the event type, but the API for this is currently being finalized. Until it's finalized, use any.
3535 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3636 public onDidChangeTreeData : vscode . Event < any > = this . _onDidChangeTreeData . event ;
37+ private previousStatus : PeersResponse | undefined = undefined ;
38+ private currentStatus : PeersResponse | undefined = undefined ;
3739
3840 constructor (
3941 private readonly ts : Tailscale ,
@@ -58,12 +60,84 @@ export class NodeExplorerProvider
5860 this . registerRefresh ( ) ;
5961 this . registerOpenDocsLink ( ) ;
6062 this . registerDownloadCommand ( ) ;
63+ this . pollForUpdates ( ) ;
6164 }
6265
6366 getTreeItem ( element : PeerBaseTreeItem ) : vscode . TreeItem {
6467 return element ;
6568 }
6669
70+ private async getPeers ( ) {
71+ this . previousStatus = this . currentStatus ;
72+ this . currentStatus = await this . ts . getPeers ( ) ;
73+ return this . currentStatus ;
74+ }
75+
76+ private async diffRelay ( ) {
77+ const status = await this . getPeers ( ) ;
78+ const prevStatus = this . previousStatus ;
79+
80+ if ( ! prevStatus ) {
81+ return false ;
82+ }
83+
84+ if ( status . Errors ) {
85+ if ( ! prevStatus . Errors || ! status . Errors . every ( ( e , i ) => e === prevStatus . Errors ?. [ i ] ) ) {
86+ return true ;
87+ }
88+ }
89+
90+ if ( status . CurrentTailnet . Name !== prevStatus . CurrentTailnet . Name ) {
91+ return true ;
92+ }
93+
94+ if ( status . CurrentTailnet . MagicDNSEnabled !== prevStatus . CurrentTailnet . MagicDNSEnabled ) {
95+ return true ;
96+ }
97+
98+ if ( status . CurrentTailnet . MagicDNSSuffix !== prevStatus . CurrentTailnet . MagicDNSSuffix ) {
99+ return true ;
100+ }
101+
102+ for ( let i = 0 ; i < status . PeerGroups . length ; ++ i ) {
103+ if ( status . PeerGroups [ i ] . Name !== prevStatus . PeerGroups [ i ] . Name ) {
104+ return true ;
105+ }
106+ if (
107+ status . PeerGroups [ i ] . Peers . length !== prevStatus . PeerGroups [ i ] . Peers . length ||
108+ ! status . PeerGroups [ i ] . Peers . every ( ( p , j ) => p . ID === prevStatus . PeerGroups [ i ] . Peers [ j ] . ID )
109+ ) {
110+ return true ;
111+ }
112+ }
113+
114+ return false ;
115+ }
116+
117+ async pollForUpdates ( ) {
118+ const interval = vscode . workspace
119+ . getConfiguration ( EXTENSION_NS )
120+ . get < number > ( 'nodeExplorer.refreshInterval' ) ;
121+ if ( interval ) {
122+ try {
123+ if ( await this . diffRelay ( ) ) {
124+ this . refresh ( ) ;
125+ }
126+ } catch ( e ) {
127+ // diffRelay might fail if the request to getPeers fails, eg.
128+ // if someone called `tailscale switch` in the the middle of
129+ // the request. If that happens, the setTimeout will cause us
130+ // to try again, so we just log this for now.
131+ Logger . error ( `could not poll for updates: ${ e } ` ) ;
132+ }
133+ // We set a timeout recursively instead of setting an interval
134+ // to avoid the case where the await expressions above take longer
135+ // than the provided interval, leading to new polls being sent out
136+ // before old ones are finished.
137+ setTimeout ( ( ) => this . pollForUpdates ( ) , interval ) ;
138+ }
139+ }
140+
67141 async getChildren ( element ?: PeerBaseTreeItem ) : Promise < PeerBaseTreeItem [ ] > {
68142 if ( element instanceof PeerErrorItem ) {
69143 return [ ] ;
@@ -144,7 +218,7 @@ export class NodeExplorerProvider
144218 const groups : PeerGroupItem [ ] = [ ] ;
145219 let hasErr = false ;
146220 try {
147- const status = await this . ts . getPeers ( ) ;
221+ const status = await this . getPeers ( ) ;
148222 if ( status . Errors && status . Errors . length ) {
149223 for ( let index = 0 ; index < status . Errors . length ; index ++ ) {
150224 const err = status . Errors [ index ] ;
0 commit comments