1- import type { Project , UserMessage , VcsFileDiff } from "@opencode-ai/sdk/v2"
1+ import type { Project , UserMessage } from "@opencode-ai/sdk/v2"
22import { useDialog } from "@opencode-ai/ui/context/dialog"
3- import { useMutation } from "@tanstack/solid-query"
3+ import { createQuery , skipToken , useMutation , useQueryClient } from "@tanstack/solid-query"
44import {
55 batch ,
66 onCleanup ,
@@ -324,6 +324,7 @@ export default function Page() {
324324 const local = useLocal ( )
325325 const file = useFile ( )
326326 const sync = useSync ( )
327+ const queryClient = useQueryClient ( )
327328 const dialog = useDialog ( )
328329 const language = useLanguage ( )
329330 const sdk = useSDK ( )
@@ -518,26 +519,6 @@ export default function Page() {
518519 deferRender : false ,
519520 } )
520521
521- const [ vcs , setVcs ] = createStore < {
522- diff : {
523- git : VcsFileDiff [ ]
524- branch : VcsFileDiff [ ]
525- }
526- ready : {
527- git : boolean
528- branch : boolean
529- }
530- } > ( {
531- diff : {
532- git : [ ] as VcsFileDiff [ ] ,
533- branch : [ ] as VcsFileDiff [ ] ,
534- } ,
535- ready : {
536- git : false ,
537- branch : false ,
538- } ,
539- } )
540-
541522 const [ followup , setFollowup ] = persisted (
542523 Persist . workspace ( sdk . directory , "followup" , [ "followup.v1" ] ) ,
543524 createStore < {
@@ -571,68 +552,6 @@ export default function Page() {
571552 let todoTimer : number | undefined
572553 let diffFrame : number | undefined
573554 let diffTimer : number | undefined
574- const vcsTask = new Map < VcsMode , Promise < void > > ( )
575- const vcsRun = new Map < VcsMode , number > ( )
576-
577- const bumpVcs = ( mode : VcsMode ) => {
578- const next = ( vcsRun . get ( mode ) ?? 0 ) + 1
579- vcsRun . set ( mode , next )
580- return next
581- }
582-
583- const resetVcs = ( mode ?: VcsMode ) => {
584- const list = mode ? [ mode ] : ( [ "git" , "branch" ] as const )
585- list . forEach ( ( item ) => {
586- bumpVcs ( item )
587- vcsTask . delete ( item )
588- setVcs ( "diff" , item , [ ] )
589- setVcs ( "ready" , item , false )
590- } )
591- }
592-
593- const loadVcs = ( mode : VcsMode , force = false ) => {
594- if ( sync . project ?. vcs !== "git" ) return Promise . resolve ( )
595- if ( ! force && vcs . ready [ mode ] ) return Promise . resolve ( )
596-
597- if ( force ) {
598- if ( vcsTask . has ( mode ) ) bumpVcs ( mode )
599- vcsTask . delete ( mode )
600- setVcs ( "ready" , mode , false )
601- }
602-
603- const current = vcsTask . get ( mode )
604- if ( current ) return current
605-
606- const run = bumpVcs ( mode )
607-
608- const task = sdk . client . vcs
609- . diff ( { mode } )
610- . then ( ( result ) => {
611- if ( vcsRun . get ( mode ) !== run ) return
612- setVcs ( "diff" , mode , list ( result . data ) )
613- setVcs ( "ready" , mode , true )
614- } )
615- . catch ( ( error ) => {
616- if ( vcsRun . get ( mode ) !== run ) return
617- console . debug ( "[session-review] failed to load vcs diff" , { mode, error } )
618- setVcs ( "diff" , mode , [ ] )
619- setVcs ( "ready" , mode , true )
620- } )
621- . finally ( ( ) => {
622- if ( vcsTask . get ( mode ) === task ) vcsTask . delete ( mode )
623- } )
624-
625- vcsTask . set ( mode , task )
626- return task
627- }
628-
629- const refreshVcs = ( ) => {
630- resetVcs ( )
631- const mode = untrack ( vcsMode )
632- if ( ! mode ) return
633- if ( ! untrack ( wantsReview ) ) return
634- void loadVcs ( mode , true )
635- }
636555
637556 createComputed ( ( prev ) => {
638557 const open = desktopReviewOpen ( )
@@ -663,21 +582,52 @@ export default function Page() {
663582 list . push ( "turn" )
664583 return list
665584 } )
585+ const mobileChanges = createMemo ( ( ) => ! isDesktop ( ) && store . mobileTab === "changes" )
586+ const wantsReview = createMemo ( ( ) =>
587+ isDesktop ( )
588+ ? desktopFileTreeOpen ( ) || ( desktopReviewOpen ( ) && activeTab ( ) === "review" )
589+ : store . mobileTab === "changes" ,
590+ )
666591 const vcsMode = createMemo < VcsMode | undefined > ( ( ) => {
667592 if ( store . changes === "git" || store . changes === "branch" ) return store . changes
668593 } )
669- const reviewDiffs = createMemo ( ( ) => {
670- if ( store . changes === "git" ) return list ( vcs . diff . git )
671- if ( store . changes === "branch" ) return list ( vcs . diff . branch )
672- return turnDiffs ( )
594+ const vcsKey = createMemo (
595+ ( ) => [ "session-vcs" , sdk . directory , sync . data . vcs ?. branch ?? "" , sync . data . vcs ?. default_branch ?? "" ] as const ,
596+ )
597+ const vcsQuery = createQuery ( ( ) => {
598+ const mode = vcsMode ( )
599+ const enabled = wantsReview ( ) && sync . project ?. vcs === "git"
600+
601+ return {
602+ queryKey : [ ...vcsKey ( ) , mode ] as const ,
603+ enabled,
604+ staleTime : Number . POSITIVE_INFINITY ,
605+ gcTime : 60 * 1000 ,
606+ queryFn : mode
607+ ? ( ) =>
608+ sdk . client . vcs
609+ . diff ( { mode } )
610+ . then ( ( result ) => list ( result . data ) )
611+ . catch ( ( error ) => {
612+ console . debug ( "[session-review] failed to load vcs diff" , { mode, error } )
613+ return [ ]
614+ } )
615+ : skipToken ,
616+ }
673617 } )
674- const reviewCount = createMemo ( ( ) => reviewDiffs ( ) . length )
675- const hasReview = createMemo ( ( ) => reviewCount ( ) > 0 )
676- const reviewReady = createMemo ( ( ) => {
677- if ( store . changes === "git" ) return vcs . ready . git
678- if ( store . changes === "branch" ) return vcs . ready . branch
618+ const refreshVcs = ( ) => void queryClient . invalidateQueries ( { queryKey : vcsKey ( ) } )
619+ const reviewDiffs = ( ) => {
620+ if ( store . changes === "git" || store . changes === "branch" )
621+ // avoids suspense
622+ return vcsQuery . isFetched ? ( vcsQuery . data ?? [ ] ) : [ ]
623+ return turnDiffs ( )
624+ }
625+ const reviewCount = ( ) => reviewDiffs ( ) . length
626+ const hasReview = ( ) => reviewCount ( ) > 0
627+ const reviewReady = ( ) => {
628+ if ( store . changes === "git" || store . changes === "branch" ) return ! vcsQuery . isPending
679629 return true
680- } )
630+ }
681631
682632 const newSessionWorktree = createMemo ( ( ) => {
683633 if ( store . newSessionWorktree === "create" ) return "create"
@@ -897,27 +847,6 @@ export default function Page() {
897847 ) ,
898848 )
899849
900- createEffect (
901- on (
902- ( ) => sdk . directory ,
903- ( ) => {
904- resetVcs ( )
905- } ,
906- { defer : true } ,
907- ) ,
908- )
909-
910- createEffect (
911- on (
912- ( ) => [ sync . data . vcs ?. branch , sync . data . vcs ?. default_branch ] as const ,
913- ( next , prev ) => {
914- if ( prev === undefined || same ( next , prev ) ) return
915- refreshVcs ( )
916- } ,
917- { defer : true } ,
918- ) ,
919- )
920-
921850 const stopVcs = sdk . event . listen ( ( evt ) => {
922851 if ( evt . details . type !== "file.watcher.updated" ) return
923852 const props =
@@ -1051,13 +980,6 @@ export default function Page() {
1051980 }
1052981 }
1053982
1054- const mobileChanges = createMemo ( ( ) => ! isDesktop ( ) && store . mobileTab === "changes" )
1055- const wantsReview = createMemo ( ( ) =>
1056- isDesktop ( )
1057- ? desktopFileTreeOpen ( ) || ( desktopReviewOpen ( ) && activeTab ( ) === "review" )
1058- : store . mobileTab === "changes" ,
1059- )
1060-
1061983 createEffect ( ( ) => {
1062984 const list = changesOptions ( )
1063985 if ( list . includes ( store . changes ) ) return
@@ -1066,22 +988,12 @@ export default function Page() {
1066988 setStore ( "changes" , next )
1067989 } )
1068990
1069- createEffect ( ( ) => {
1070- const mode = vcsMode ( )
1071- if ( ! mode ) return
1072- if ( ! wantsReview ( ) ) return
1073- void loadVcs ( mode )
1074- } )
1075-
1076991 createEffect (
1077992 on (
1078993 ( ) => sync . data . session_status [ params . id ?? "" ] ?. type ,
1079994 ( next , prev ) => {
1080- const mode = vcsMode ( )
1081- if ( ! mode ) return
1082- if ( ! wantsReview ( ) ) return
1083995 if ( next !== "idle" || prev === undefined || prev === "idle" ) return
1084- void loadVcs ( mode , true )
996+ refreshVcs ( )
1085997 } ,
1086998 { defer : true } ,
1087999 ) ,
0 commit comments