@@ -3,10 +3,11 @@ import { ENABLE_GIT_BLAME } from '@core/constant/featureFlags';
33import { macroIdToEmail , tryMacroId , useDisplayNameParts } from '@core/user' ;
44import { formatRelativeTimestamp } from '@entity' ;
55import { syncServiceClient } from '@service-sync/client' ;
6- import { debounce } from '@solid-primitives/scheduled' ;
6+ import { createScheduled , debounce } from '@solid-primitives/scheduled' ;
7+ import { createQuery } from '@tanstack/solid-query' ;
78import {
89 createEffect ,
9- createResource ,
10+ createMemo ,
1011 createSignal ,
1112 onCleanup ,
1213 Show ,
@@ -37,49 +38,37 @@ export function BlameTooltip(props: {
3738 documentId : string ;
3839} ) {
3940 const [ visible , setVisible ] = createSignal ( false ) ;
40- // The nodeId we've actually committed to fetching for. Debounced from the
41- // raw hovered nodeId so we don't fire a request the instant the cursor
42- // crosses a text node.
43- const [ armedNodeId , setArmedNodeId ] = createSignal < string | null > ( null ) ;
4441 let shownAtX = 0 ;
4542 let shownAtY = 0 ;
4643 let showTimer : ReturnType < typeof setTimeout > | null = null ;
4744
48- const debouncedArm = debounce ( ( nodeId : string | null ) => {
49- setArmedNodeId ( nodeId ) ;
50- } , FETCH_DELAY_MS ) ;
45+ // Only commits to a new nodeId after FETCH_DELAY_MS of hover stillness.
46+ const scheduled = createScheduled ( fn => debounce ( fn , FETCH_DELAY_MS ) ) ;
47+ const queriedNodeId = createMemo < string | null > ( ( prev ) => {
48+ const active = props . state . hovering ? props . state . nodeId : null ;
49+ return scheduled ( ) ? active : prev ;
50+ } , null ) ;
5151
5252 const hide = ( ) => {
53- debouncedArm . clear ( ) ;
5453 if ( showTimer ) clearTimeout ( showTimer ) ;
5554 showTimer = null ;
56- setArmedNodeId ( null ) ;
5755 setVisible ( false ) ;
5856 } ;
5957
60- const [ blame ] = createResource (
61- ( ) => ( ENABLE_GIT_BLAME ( ) ? armedNodeId ( ) : null ) ,
62- async ( nodeId ) => {
58+ const query = createQuery ( ( ) => ( {
59+ queryKey : [ 'blame' , props . documentId , queriedNodeId ( ) ] ,
60+ queryFn : async ( ) => {
6361 const res = await syncServiceClient . getNodeBlame ( {
6462 documentId : props . documentId ,
65- nodeId,
63+ nodeId : queriedNodeId ( ) ! ,
6664 } ) ;
6765 return res . isOk ( ) ? res . value : null ;
68- }
69- ) ;
70-
71- // Drive the fetch — debounced on nodeId only, ignores cursor x/y.
72- createEffect ( ( ) => {
73- const nodeId = props . state . hovering ? props . state . nodeId : null ;
74- if ( nodeId === null ) {
75- debouncedArm . clear ( ) ;
76- setArmedNodeId ( null ) ;
77- } else {
78- debouncedArm ( nodeId ) ;
79- }
80- } ) ;
66+ } ,
67+ enabled : ENABLE_GIT_BLAME ( ) && queriedNodeId ( ) !== null ,
68+ staleTime : Infinity ,
69+ } ) ) ;
8170
82- // Drive the visibility — based on cursor stillness (x/y).
71+ // Drive visibility based on cursor stillness (x/y).
8372 createEffect ( ( ) => {
8473 const { hovering, x, y } = props . state ;
8574
@@ -103,7 +92,7 @@ export function BlameTooltip(props: {
10392 onCleanup ( hide ) ;
10493
10594 return (
106- < Show when = { visible ( ) && blame ( ) ?. userId ? blame ( ) : null } >
95+ < Show when = { visible ( ) && query . data ?. userId ? query . data : null } >
10796 { ( b ) => (
10897 < div
10998 class = "fixed z-50 text-xs text-ink-secondary/70 pointer-events-none"
0 commit comments