mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 18:37:24 +00:00
feat(cloud): back from GitHub, the new repository shows up and is picked (REPO-ACCESS-REFRESH)
After 'Add a GitHub repository…' the composer waits for GitHub: 'Waiting for GitHub…' under Project, the list re-reads when the window regains focus and every 4 s while focused (paused when hidden), for up to 3 minutes. The first repository that wasn't listed before is picked and Run on follows it. Picking or clearing a repository ends the wait.
This commit is contained in:
+11
-3
@@ -10,7 +10,7 @@ import type { CloudOnlyRepo } from './run-on/use-cloud-only-repo'
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
function cloudOnly(moreAccess: () => void): CloudOnlyRepo {
|
||||
function cloudOnly(moreAccess: () => void, waitingForGitHub = false): CloudOnlyRepo {
|
||||
return {
|
||||
repos: [],
|
||||
showMore: null,
|
||||
@@ -20,11 +20,12 @@ function cloudOnly(moreAccess: () => void): CloudOnlyRepo {
|
||||
pick: () => {},
|
||||
clear: () => {},
|
||||
moreAccess,
|
||||
waitingForGitHub,
|
||||
start: async () => false
|
||||
}
|
||||
}
|
||||
|
||||
function renderSection(recipeId: string | null, moreAccess: () => void): void {
|
||||
function renderSection(recipeId: string | null, moreAccess: () => void, waiting = false): void {
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<NewWorkspaceComposerProjectSection
|
||||
@@ -50,7 +51,7 @@ function renderSection(recipeId: string | null, moreAccess: () => void): void {
|
||||
cloudStart={{
|
||||
onSelectOrcaCloud: () => {},
|
||||
onUserPick: () => {},
|
||||
cloudOnly: cloudOnly(moreAccess)
|
||||
cloudOnly: cloudOnly(moreAccess, waiting)
|
||||
}}
|
||||
handleAddSshHost={() => {}}
|
||||
handleAddRemoteServer={() => {}}
|
||||
@@ -92,3 +93,10 @@ describe('the Add project button beside Project (PROJ-MENU part 2, n29)', () =>
|
||||
expect(screen.queryByText('Add a GitHub repository…')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('back from GitHub (REPO-ACCESS-REFRESH)', () => {
|
||||
it('says "Waiting for GitHub…" under Project while the list re-reads', () => {
|
||||
renderSection(ORCA_CLOUD_RECIPE_ID, vi.fn(), true)
|
||||
expect(screen.getByRole('status').textContent).toBe('Waiting for GitHub…')
|
||||
})
|
||||
})
|
||||
|
||||
+34
-2
@@ -8,6 +8,7 @@ import {
|
||||
import { NewWorkspaceComposerProjectSection } from './NewWorkspaceComposerProjectSection'
|
||||
import type { EphemeralVmRecipeOption } from './run-target-options'
|
||||
import type { OrcaCloudRunTargetAccess } from './orca-cloud-run-target-access'
|
||||
import type { CloudStartView } from './run-on/cloud-start-view'
|
||||
|
||||
const projects: NewWorkspaceProjectOption[] = [
|
||||
{
|
||||
@@ -87,17 +88,37 @@ const orcaCloudRecipe: EphemeralVmRecipeOption = {
|
||||
create: 'orca-cloud create'
|
||||
}
|
||||
|
||||
const waitingCloudStart: CloudStartView = {
|
||||
onSelectOrcaCloud: () => {},
|
||||
onUserPick: () => {},
|
||||
cloudOnly: {
|
||||
repos: [],
|
||||
showMore: null,
|
||||
picked: null,
|
||||
folderProject: false,
|
||||
githubStep: null,
|
||||
pick: () => {},
|
||||
clear: () => {},
|
||||
moreAccess: () => {},
|
||||
waitingForGitHub: true,
|
||||
start: async () => false
|
||||
}
|
||||
}
|
||||
|
||||
function ProjectAndHostPreview({
|
||||
folderOnly = false,
|
||||
orcaCloud
|
||||
orcaCloud,
|
||||
waitingForGitHub = false
|
||||
}: {
|
||||
folderOnly?: boolean
|
||||
/** Back from GitHub's access page (REPO-ACCESS-REFRESH). */
|
||||
waitingForGitHub?: boolean
|
||||
/** Offers Orca Cloud with this entitlement. */
|
||||
orcaCloud?: OrcaCloudRunTargetAccess
|
||||
}): React.JSX.Element {
|
||||
const [project, setProject] = useState(folderOnly ? 'folder' : 'orca')
|
||||
const [host, setHost] = useState('local')
|
||||
const [recipe, setRecipe] = useState<string | null>(null)
|
||||
const [recipe, setRecipe] = useState<string | null>(waitingForGitHub ? 'cloud' : null)
|
||||
return (
|
||||
<div className="space-y-4 rounded-xl border border-border bg-background p-4">
|
||||
<h1 className="text-base font-semibold">Create workspace</h1>
|
||||
@@ -129,6 +150,7 @@ function ProjectAndHostPreview({
|
||||
selectedEphemeralVmRecipeId={recipe}
|
||||
onEphemeralVmRecipeChange={setRecipe}
|
||||
{...(orcaCloud ? { orcaCloudAccess: orcaCloud } : {})}
|
||||
{...(waitingForGitHub ? { cloudStart: waitingCloudStart } : {})}
|
||||
handleAddSshHost={() => {}}
|
||||
handleAddRemoteServer={() => {}}
|
||||
handleConnectRunTargetHost={async () => {}}
|
||||
@@ -215,3 +237,13 @@ export const hostMenuOrcaCloudNoPlanMember = defineFixture({
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export const waitingForGitHub = defineFixture({
|
||||
...closed,
|
||||
render: () => (
|
||||
<ProjectAndHostPreview
|
||||
orcaCloud={{ state: 'entitled', remainingSeconds: 41 * 3600 }}
|
||||
waitingForGitHub
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -239,6 +239,15 @@ export function NewWorkspaceComposerProjectSection({
|
||||
<p id={projectDescriptionId} className="text-[11px] text-destructive">
|
||||
{projectError}
|
||||
</p>
|
||||
) : cloudOnly?.waitingForGitHub ? (
|
||||
// REPO-ACCESS-REFRESH: back from GitHub's access page, until the repository shows up.
|
||||
<p
|
||||
role="status"
|
||||
className="flex items-center gap-1.5 text-[11px] text-muted-foreground"
|
||||
>
|
||||
<LoaderCircle className="size-3 animate-spin motion-reduce:animate-none" />
|
||||
{t('components.newWorkspace.cloudOnly.waitingForGitHub', 'Waiting for GitHub…')}
|
||||
</p>
|
||||
) : needsRepo ? (
|
||||
<OrcaCloudNeedsRepoLine
|
||||
hasRepos={needsRepo.hasRepos}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@/lib/orca-cloud/use-orca-cloud-github-repositories'
|
||||
import { startCloudWorkspaceFromGitHub } from '@/lib/orca-cloud/start-cloud-workspace-from-github'
|
||||
import type { CloudSetupStep } from './cloud-start-model'
|
||||
import { useGitHubAccessHandoff } from './use-github-access-handoff'
|
||||
|
||||
export type CloudOnlyRepo = {
|
||||
repos: readonly OrcaCloudGitHubRepository[] | null
|
||||
@@ -24,6 +25,8 @@ export type CloudOnlyRepo = {
|
||||
pick: (fullName: string) => void
|
||||
clear: () => void
|
||||
moreAccess: () => void
|
||||
/** Back from GitHub's access page: the list re-reads until the new repository shows up. */
|
||||
waitingForGitHub: boolean
|
||||
start: (request: { prompt: string; agent: string | null }) => Promise<boolean>
|
||||
}
|
||||
|
||||
@@ -32,7 +35,8 @@ export function useCloudOnlyRepo({
|
||||
initialPicked = null,
|
||||
cloudSelected,
|
||||
cloudOffered,
|
||||
signedIn
|
||||
signedIn,
|
||||
onAutoPick
|
||||
}: {
|
||||
repoId: string
|
||||
/** `owner/repo` picked when the composer opens (Back to draft from its build sheet). */
|
||||
@@ -43,12 +47,23 @@ export function useCloudOnlyRepo({
|
||||
cloudOffered: boolean
|
||||
/** The list needs an account; signed out, setup comes first. */
|
||||
signedIn: boolean
|
||||
/** A repository granted on GitHub was picked for the person: Run on becomes Orca Cloud. */
|
||||
onAutoPick?: () => void
|
||||
}): CloudOnlyRepo {
|
||||
const { folderProject, folderStep } = useFolderGitHubStep(repoId)
|
||||
// D4 rows show while Orca Cloud is picked, or for a folder, which can only start from GitHub.
|
||||
const enabled = (cloudSelected || folderProject) && cloudOffered
|
||||
const { repos, showMore } = useOrcaCloudGitHubRepositories(enabled && signedIn)
|
||||
const [refreshSignal, setRefreshSignal] = useState(0)
|
||||
const { repos, showMore } = useOrcaCloudGitHubRepositories(enabled && signedIn, refreshSignal)
|
||||
const [pickedName, setPickedName] = useState<string | null>(initialPicked)
|
||||
const handoff = useGitHubAccessHandoff({
|
||||
repos,
|
||||
onReread: () => setRefreshSignal((n) => n + 1),
|
||||
onNewRepository: (fullName) => {
|
||||
setPickedName(fullName)
|
||||
onAutoPick?.()
|
||||
}
|
||||
})
|
||||
const picked = useMemo(
|
||||
() => (enabled ? (repos?.find((repo) => repo.fullName === pickedName) ?? null) : null),
|
||||
[enabled, pickedName, repos]
|
||||
@@ -65,11 +80,23 @@ export function useCloudOnlyRepo({
|
||||
: null,
|
||||
[picked]
|
||||
)
|
||||
const clear = useCallback(() => setPickedName(null), [])
|
||||
const { begin: beginHandoff, cancel: cancelHandoff } = handoff
|
||||
const clear = useCallback(() => {
|
||||
cancelHandoff()
|
||||
setPickedName(null)
|
||||
}, [cancelHandoff])
|
||||
const pick = useCallback(
|
||||
(fullName: string) => {
|
||||
cancelHandoff()
|
||||
setPickedName(fullName)
|
||||
},
|
||||
[cancelHandoff]
|
||||
)
|
||||
const moreAccess = useCallback(() => {
|
||||
invalidateOrcaCloudGitHubRepositories()
|
||||
beginHandoff()
|
||||
void window.api.orcaCloudWorkspace.connectGitHub()
|
||||
}, [])
|
||||
}, [beginHandoff])
|
||||
const start = useCallback(
|
||||
(request: { prompt: string; agent: string | null }) =>
|
||||
picked
|
||||
@@ -88,9 +115,10 @@ export function useCloudOnlyRepo({
|
||||
picked,
|
||||
githubStep: githubStep ?? folderStep,
|
||||
folderProject,
|
||||
pick: setPickedName,
|
||||
pick,
|
||||
clear,
|
||||
moreAccess,
|
||||
waitingForGitHub: enabled && handoff.waiting,
|
||||
start
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// dialog: Start either creates the workspace (everything is set up) or saves the draft, closes the
|
||||
// dialog and opens the full-page setup, which starts the prompt by itself when done. Also: where
|
||||
// each project last started (O5), the remembered default (D7) and GitHub-only repositories (D4).
|
||||
import { useCallback, useEffect, useMemo } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { useAppStore } from '@/store/index'
|
||||
import { clearCloudStartDraft, saveCloudStartDraft } from '@/lib/orca-cloud/cloud-start-draft'
|
||||
import {
|
||||
@@ -89,12 +89,15 @@ export function useCloudStartComposer({
|
||||
const fetched = useOrcaCloudRunTargetAccess(segment === 'enabled' && !accessOverride)
|
||||
const access = accessOverride ?? fetched
|
||||
const githubProjectCount = useAppStore((s) => s.repos.filter(isGitHubBackedProject).length)
|
||||
const selectOrcaCloudRef = useRef<() => void>(() => {})
|
||||
const cloudOnly = useCloudOnlyRepo({
|
||||
repoId,
|
||||
initialPicked: initialGitHubRepo,
|
||||
cloudSelected,
|
||||
cloudOffered: segment === 'enabled',
|
||||
signedIn: availability !== 'needs-sign-in' && access.state !== 'signed-out'
|
||||
signedIn: availability !== 'needs-sign-in' && access.state !== 'signed-out',
|
||||
// A repository granted on GitHub is picked for the person, so Run on follows it.
|
||||
onAutoPick: () => selectOrcaCloudRef.current()
|
||||
})
|
||||
const { steps: rowSteps } = useCloudStartSteps({
|
||||
availability,
|
||||
@@ -135,6 +138,9 @@ export function useCloudStartComposer({
|
||||
markTouched()
|
||||
setRecipeId?.(ORCA_CLOUD_RECIPE_ID)
|
||||
}, [markTouched, setRecipeId])
|
||||
useEffect(() => {
|
||||
selectOrcaCloudRef.current = onSelectOrcaCloud
|
||||
}, [onSelectOrcaCloud])
|
||||
|
||||
// Start with setup still open: the dialog closes and the page takes over, prompt kept (N6).
|
||||
const openSetup = useCallback(
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { act, cleanup, renderHook } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { OrcaCloudGitHubRepository } from '@/lib/orca-cloud/use-orca-cloud-github-repositories'
|
||||
|
||||
const invalidate = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/lib/orca-cloud/use-orca-cloud-github-repositories', () => ({
|
||||
invalidateOrcaCloudGitHubRepositories: invalidate
|
||||
}))
|
||||
|
||||
import {
|
||||
GITHUB_HANDOFF_POLL_MS,
|
||||
GITHUB_HANDOFF_WAIT_MS,
|
||||
useGitHubAccessHandoff
|
||||
} from './use-github-access-handoff'
|
||||
|
||||
function repo(fullName: string): OrcaCloudGitHubRepository {
|
||||
return { fullName, url: `https://github.com/${fullName}`, private: true }
|
||||
}
|
||||
|
||||
function setup(initial: OrcaCloudGitHubRepository[] | null) {
|
||||
const onReread = vi.fn()
|
||||
const onNewRepository = vi.fn()
|
||||
const hook = renderHook(
|
||||
({ repos }: { repos: OrcaCloudGitHubRepository[] | null }) =>
|
||||
useGitHubAccessHandoff({ repos, onReread, onNewRepository }),
|
||||
{ initialProps: { repos: initial } }
|
||||
)
|
||||
return { hook, onReread, onNewRepository }
|
||||
}
|
||||
|
||||
describe('REPO-ACCESS-REFRESH: back from GitHub, the new repository shows up and is picked', () => {
|
||||
let focused = true
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
focused = true
|
||||
vi.spyOn(document, 'hasFocus').mockImplementation(() => focused)
|
||||
})
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.useRealTimers()
|
||||
vi.restoreAllMocks()
|
||||
invalidate.mockReset()
|
||||
})
|
||||
|
||||
it('re-reads on focus and picks the repository that was not listed before', () => {
|
||||
const { hook, onReread, onNewRepository } = setup([repo('acme/api')])
|
||||
act(() => hook.result.current.begin())
|
||||
expect(hook.result.current.waiting).toBe(true)
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event('focus'))
|
||||
})
|
||||
expect(invalidate).toHaveBeenCalled()
|
||||
expect(onReread).toHaveBeenCalledOnce()
|
||||
// The same list again: still waiting.
|
||||
hook.rerender({ repos: [repo('acme/api')] })
|
||||
expect(hook.result.current.waiting).toBe(true)
|
||||
hook.rerender({ repos: [repo('acme/api'), repo('acme/web')] })
|
||||
expect(onNewRepository).toHaveBeenCalledWith('acme/web')
|
||||
expect(hook.result.current.waiting).toBe(false)
|
||||
})
|
||||
|
||||
it('polls only while this window has focus, and gives up after a while', () => {
|
||||
const { hook, onReread, onNewRepository } = setup([])
|
||||
act(() => hook.result.current.begin())
|
||||
focused = false
|
||||
act(() => vi.advanceTimersByTime(GITHUB_HANDOFF_POLL_MS * 3))
|
||||
expect(onReread).not.toHaveBeenCalled()
|
||||
focused = true
|
||||
act(() => vi.advanceTimersByTime(GITHUB_HANDOFF_POLL_MS))
|
||||
expect(onReread).toHaveBeenCalledOnce()
|
||||
act(() => vi.advanceTimersByTime(GITHUB_HANDOFF_WAIT_MS))
|
||||
expect(hook.result.current.waiting).toBe(false)
|
||||
const calls = onReread.mock.calls.length
|
||||
act(() => vi.advanceTimersByTime(GITHUB_HANDOFF_POLL_MS * 2))
|
||||
expect(onReread.mock.calls.length).toBe(calls)
|
||||
expect(onNewRepository).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('takes the first answer as the baseline when the list had not loaded yet', () => {
|
||||
const { hook, onNewRepository } = setup(null)
|
||||
act(() => hook.result.current.begin())
|
||||
hook.rerender({ repos: [repo('acme/api')] })
|
||||
expect(onNewRepository).not.toHaveBeenCalled()
|
||||
hook.rerender({ repos: [repo('acme/api'), repo('acme/new')] })
|
||||
expect(onNewRepository).toHaveBeenCalledWith('acme/new')
|
||||
})
|
||||
|
||||
it('stops waiting when the person picks or clears on their own', () => {
|
||||
const { hook, onNewRepository } = setup([repo('acme/api')])
|
||||
act(() => hook.result.current.begin())
|
||||
act(() => hook.result.current.cancel())
|
||||
hook.rerender({ repos: [repo('acme/api'), repo('acme/web')] })
|
||||
expect(onNewRepository).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,100 @@
|
||||
// REPO-ACCESS-REFRESH: after "Add a GitHub repository…" the person grants access on GitHub and
|
||||
// comes back. The list reads GitHub again when this window regains focus, and every few seconds
|
||||
// while it has focus, until a repository shows up that wasn't listed before. That one is picked.
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
invalidateOrcaCloudGitHubRepositories,
|
||||
type OrcaCloudGitHubRepository
|
||||
} from '@/lib/orca-cloud/use-orca-cloud-github-repositories'
|
||||
import { installWindowVisibilityInterval } from '@/lib/window-activity/window-visibility-interval'
|
||||
|
||||
/** While focused, ask GitHub again this often (GitHub's install webhook can lag the redirect). */
|
||||
export const GITHUB_HANDOFF_POLL_MS = 4_000
|
||||
/** Stop waiting after this long: the person may have closed GitHub without granting anything. */
|
||||
export const GITHUB_HANDOFF_WAIT_MS = 3 * 60_000
|
||||
|
||||
function names(repos: readonly OrcaCloudGitHubRepository[]): Set<string> {
|
||||
return new Set(repos.map((repo) => repo.fullName))
|
||||
}
|
||||
|
||||
export function useGitHubAccessHandoff({
|
||||
repos,
|
||||
onReread,
|
||||
onNewRepository
|
||||
}: {
|
||||
repos: readonly OrcaCloudGitHubRepository[] | null
|
||||
/** Read the list again (its cache was just dropped). */
|
||||
onReread: () => void
|
||||
onNewRepository: (fullName: string) => void
|
||||
}): {
|
||||
waiting: boolean
|
||||
begin: () => void
|
||||
cancel: () => void
|
||||
} {
|
||||
const [waiting, setWaiting] = useState(false)
|
||||
// Listed before the handoff; null when the list hadn't loaded yet (the next answer is the baseline).
|
||||
const knownRef = useRef<ReadonlySet<string> | null>(null)
|
||||
const reposRef = useRef(repos)
|
||||
const onNewRef = useRef(onNewRepository)
|
||||
const onRereadRef = useRef(onReread)
|
||||
useEffect(() => {
|
||||
reposRef.current = repos
|
||||
onNewRef.current = onNewRepository
|
||||
onRereadRef.current = onReread
|
||||
})
|
||||
|
||||
const begin = useCallback(() => {
|
||||
knownRef.current = reposRef.current ? names(reposRef.current) : null
|
||||
setWaiting(true)
|
||||
}, [])
|
||||
const cancel = useCallback(() => setWaiting(false), [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!waiting) {
|
||||
return
|
||||
}
|
||||
const reread = (): void => {
|
||||
invalidateOrcaCloudGitHubRepositories()
|
||||
onRereadRef.current()
|
||||
}
|
||||
// Away on GitHub, polling would only spend requests; coming back fires focus.
|
||||
const stopPolling = installWindowVisibilityInterval({
|
||||
run: () => {
|
||||
if (document.hasFocus()) {
|
||||
reread()
|
||||
}
|
||||
},
|
||||
// Not on install (the person is leaving for GitHub) or on becoming visible: focus covers the return.
|
||||
runOnVisible: () => {},
|
||||
intervalMs: GITHUB_HANDOFF_POLL_MS
|
||||
})
|
||||
const giveUp = window.setTimeout(cancel, GITHUB_HANDOFF_WAIT_MS)
|
||||
window.addEventListener('focus', reread)
|
||||
return () => {
|
||||
stopPolling()
|
||||
window.clearTimeout(giveUp)
|
||||
window.removeEventListener('focus', reread)
|
||||
}
|
||||
}, [cancel, waiting])
|
||||
|
||||
// Each answer while waiting: the first one may only set the baseline; a new name is the grant.
|
||||
useEffect(() => {
|
||||
if (!waiting || !repos) {
|
||||
return
|
||||
}
|
||||
const known = knownRef.current
|
||||
if (!known) {
|
||||
knownRef.current = names(repos)
|
||||
return
|
||||
}
|
||||
const added = repos.find((repo) => !known.has(repo.fullName))
|
||||
if (added) {
|
||||
// The list is GitHub's answer to a re-read, not a prop to mirror: the wait ends with it.
|
||||
// oxlint-disable-next-line react-doctor/no-adjust-state-on-prop-change
|
||||
setWaiting(false)
|
||||
onNewRef.current(added.fullName)
|
||||
}
|
||||
}, [repos, waiting])
|
||||
|
||||
return { waiting, begin, cancel }
|
||||
}
|
||||
@@ -23928,7 +23928,8 @@
|
||||
"rowPublic": "{{repo}}, cloud only",
|
||||
"privateLabel": "Private",
|
||||
"menuGroup": "Orca Cloud",
|
||||
"addRepositoryMenu": "Add a GitHub repository…"
|
||||
"addRepositoryMenu": "Add a GitHub repository…",
|
||||
"waitingForGitHub": "Waiting for GitHub…"
|
||||
},
|
||||
"agentLabel": "Agent"
|
||||
},
|
||||
|
||||
@@ -93,7 +93,11 @@ export function selectCloudOnlyRepositories(
|
||||
.sort((a, b) => a.fullName.localeCompare(b.fullName))
|
||||
}
|
||||
|
||||
export function useOrcaCloudGitHubRepositories(enabled: boolean): {
|
||||
/** `refreshSignal`: each change reads the list again (after the cache was invalidated). */
|
||||
export function useOrcaCloudGitHubRepositories(
|
||||
enabled: boolean,
|
||||
refreshSignal = 0
|
||||
): {
|
||||
repos: OrcaCloudGitHubRepository[] | null
|
||||
/** Present while GitHub has more pages than are listed. */
|
||||
showMore: (() => void) | null
|
||||
@@ -116,7 +120,7 @@ export function useOrcaCloudGitHubRepositories(enabled: boolean): {
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [enabled, identity, moreRequests])
|
||||
}, [enabled, identity, moreRequests, refreshSignal])
|
||||
const visible = useMemo(
|
||||
() =>
|
||||
enabled && listing
|
||||
|
||||
Reference in New Issue
Block a user