@@ -92,6 +92,9 @@ class QueryRenderChild extends MarkdownRenderChild {
92
92
private renderEventRef : EventRef | undefined ;
93
93
private queryReloadTimeout : NodeJS . Timeout | undefined ;
94
94
95
+ private isCacheChangedSinceLastRedraw = false ;
96
+ private observer : IntersectionObserver | null = null ;
97
+
95
98
private readonly queryResultsRenderer : QueryResultsRenderer ;
96
99
97
100
constructor ( {
@@ -162,6 +165,38 @@ class QueryRenderChild extends MarkdownRenderChild {
162
165
this . handleMetadataOrFilePathChange ( tFile . path , fileCache ) ;
163
166
} ) ,
164
167
) ;
168
+
169
+ this . setupVisibilityObserver ( ) ;
170
+ }
171
+
172
+ private setupVisibilityObserver ( ) {
173
+ if ( this . observer ) {
174
+ return ;
175
+ }
176
+
177
+ this . observer = new IntersectionObserver ( ( [ entry ] ) => {
178
+ if ( ! this . containerEl . isShown ( ) ) {
179
+ return ;
180
+ }
181
+
182
+ // entry describes a single visibility change for the specific element we are observing.
183
+ // It is safe to assume `entry.target === this.containerEl` here.
184
+ if ( ! entry . isIntersecting ) {
185
+ return ;
186
+ }
187
+
188
+ this . queryResultsRenderer . query . debug (
189
+ `[render][observer] Became visible, isCacheChangedSinceLastRedraw:${ this . isCacheChangedSinceLastRedraw } ` ,
190
+ ) ;
191
+ if ( this . isCacheChangedSinceLastRedraw ) {
192
+ this . queryResultsRenderer . query . debug ( '[render][observer] ... updating search results' ) ;
193
+ this . render ( { tasks : this . plugin . getTasks ( ) , state : this . plugin . getState ( ) } )
194
+ . then ( )
195
+ . catch ( ( e ) => console . error ( e ) ) ;
196
+ }
197
+ } ) ;
198
+
199
+ this . observer . observe ( this . containerEl ) ;
165
200
}
166
201
167
202
private handleMetadataOrFilePathChange ( filePath : string , fileCache : CachedMetadata | null ) {
@@ -189,6 +224,9 @@ class QueryRenderChild extends MarkdownRenderChild {
189
224
if ( this . queryReloadTimeout !== undefined ) {
190
225
clearTimeout ( this . queryReloadTimeout ) ;
191
226
}
227
+
228
+ this . observer ?. disconnect ( ) ;
229
+ this . observer = null ;
192
230
}
193
231
194
232
/**
@@ -219,6 +257,42 @@ class QueryRenderChild extends MarkdownRenderChild {
219
257
}
220
258
221
259
private async render ( { tasks, state } : { tasks : Task [ ] ; state : State } ) {
260
+ // We got here because the Cache reported a change in at least one task in the vault.
261
+ // So note that any results we have already drawn are now out-of-date:
262
+ this . isCacheChangedSinceLastRedraw = true ;
263
+
264
+ requestAnimationFrame ( async ( ) => {
265
+ // We have to wrap the rendering inside requestAnimationFrame() to ensure
266
+ // that we get correct values for isConnected and isShown().
267
+ if ( ! this . containerEl . isConnected ) {
268
+ // Example reasons why we might not be "connected":
269
+ // - This Tasks query block is contained within another plugin's code block,
270
+ // such as a Tabs plugin. The file is closed and that plugin has not correctly
271
+ // tidied up, so we have not been deleted.
272
+ this . queryResultsRenderer . query . debug (
273
+ '[render] Ignoring redraw request, as code block is not connected.' ,
274
+ ) ;
275
+ return ;
276
+ }
277
+
278
+ if ( ! this . containerEl . isShown ( ) ) {
279
+ // Example reasons why we might not be "shown":
280
+ // - We are in a collapsed callout.
281
+ // - We are in a note which is obscured by another note.
282
+ // - We are in a Tabs plugin, in a tab which is not at the front.
283
+ // - The user has not yet scrolled to this code block's position in the file.
284
+ this . queryResultsRenderer . query . debug ( '[render] Ignoring redraw request, as code block is not shown.' ) ;
285
+ return ;
286
+ }
287
+
288
+ await this . renderResults ( state , tasks ) ;
289
+
290
+ // Our results are now up-to-date:
291
+ this . isCacheChangedSinceLastRedraw = false ;
292
+ } ) ;
293
+ }
294
+
295
+ private async renderResults ( state : State , tasks : Task [ ] ) {
222
296
const content = createAndAppendElement ( 'div' , this . containerEl ) ;
223
297
await this . queryResultsRenderer . render ( state , tasks , content , {
224
298
allTasks : this . plugin . getTasks ( ) ,
0 commit comments