Fix "Missing queryFn" error when reloading with project openn (#10894)

- Fix https://github.com/enso-org/cloud-v2/issues/1451
- Fix "Missing queryFn" error due to
- This is done by providing a default value to the query. A more ideal solution may be to use the full query with the `queryFn`, but this is not an option here as the full query for this query requires a lot more info, some of which we don't have.

# Important Notes
- Test opening a top-level project and then refreshing
- Test opening a project in a nested directory and then refreshing (to make sure the query is not being executed just because the corresponding row is visible)
This commit is contained in:
somebody1234 2024-08-27 17:40:27 +10:00 committed by GitHub
parent 4d286e02b6
commit bd3ebc5000
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 22 additions and 32 deletions

View File

@ -169,7 +169,7 @@ export default function AssetRow(props: AssetRowProps) {
// This is SAFE, as `isOpened` is only true for projects. // This is SAFE, as `isOpened` is only true for projects.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
...createGetProjectDetailsQuery.createPassiveListener(item.item.id as backendModule.ProjectId), ...createGetProjectDetailsQuery.createPassiveListener(item.item.id as backendModule.ProjectId),
select: (data) => data.state.type, select: (data) => data?.state.type,
enabled: item.type === backendModule.AssetType.project, enabled: item.type === backendModule.AssetType.project,
}) })

View File

@ -74,13 +74,9 @@ export default function ProjectIcon(props: ProjectIconProps) {
const { user } = authProvider.useFullUserSession() const { user } = authProvider.useFullUserSession()
const { getText } = textProvider.useText() const { getText } = textProvider.useText()
const { const { data: projectState, isError } = reactQuery.useQuery({
data: projectState,
isLoading,
isError,
} = reactQuery.useQuery({
...projectHooks.createGetProjectDetailsQuery.createPassiveListener(item.id), ...projectHooks.createGetProjectDetailsQuery.createPassiveListener(item.id),
select: (data) => data.state, select: (data) => data?.state,
enabled: isOpened, enabled: isOpened,
}) })
const status = projectState?.type const status = projectState?.type
@ -95,12 +91,8 @@ export default function ProjectIcon(props: ProjectIconProps) {
// Project is closed, show open button // Project is closed, show open button
if (!isOpened) { if (!isOpened) {
return backendModule.ProjectState.closed return backendModule.ProjectState.closed
} else if (!isLoading && status == null) {
// Project is opened, but not yet queried.
return backendModule.ProjectState.openInProgress
} else if (isLoading) {
return backendModule.ProjectState.openInProgress
} else if (status == null) { } else if (status == null) {
// Project is opened, but not yet queried.
return backendModule.ProjectState.openInProgress return backendModule.ProjectState.openInProgress
} else if (status === backendModule.ProjectState.closed) { } else if (status === backendModule.ProjectState.closed) {
// Project is opened locally, but not on the backend yet. // Project is opened locally, but not on the backend yet.
@ -113,8 +105,6 @@ export default function ProjectIcon(props: ProjectIconProps) {
const spinnerState = (() => { const spinnerState = (() => {
if (!isOpened) { if (!isOpened) {
return spinner.SpinnerState.initial return spinner.SpinnerState.initial
} else if (isLoading) {
return spinner.SpinnerState.loadingSlow
} else if (isError) { } else if (isError) {
return spinner.SpinnerState.initial return spinner.SpinnerState.initial
} else if (status == null) { } else if (status == null) {

View File

@ -28,6 +28,14 @@ import type RemoteBackend from '#/services/RemoteBackend'
// ==================================== // ====================================
// === createGetProjectDetailsQuery === // === createGetProjectDetailsQuery ===
// ==================================== // ====================================
/** Default interval for refetching project status when the project is opened. */
const OPENED_INTERVAL_MS = 30_000
/** Interval when we open a cloud project.
* Since opening a cloud project is a long operation, we want to check the status less often. */
const CLOUD_OPENING_INTERVAL_MS = 5_000
/** Interval when we open a local project or when we want to sync the project status as soon as
* possible. */
const ACTIVE_SYNC_INTERVAL_MS = 100
/** Options for {@link createGetProjectDetailsQuery}. */ /** Options for {@link createGetProjectDetailsQuery}. */
export interface CreateOpenedProjectQueryOptions { export interface CreateOpenedProjectQueryOptions {
@ -51,14 +59,6 @@ export function createGetProjectDetailsQuery(options: CreateOpenedProjectQueryOp
meta: { persist: false }, meta: { persist: false },
gcTime: 0, gcTime: 0,
refetchInterval: ({ state }) => { refetchInterval: ({ state }) => {
/** Default interval for refetching project status when the project is opened. */
const openedIntervalMS = 30_000
/** Interval when we open a cloud project.
* Since opening a cloud project is a long operation, we want to check the status less often. */
const cloudOpeningIntervalMS = 5_000
/** Interval when we open a local project or when we want to sync the project status as soon as
* possible. */
const activeSyncIntervalMS = 100
const states = [backendModule.ProjectState.opened, backendModule.ProjectState.closed] const states = [backendModule.ProjectState.opened, backendModule.ProjectState.closed]
if (state.status === 'error') { if (state.status === 'error') {
@ -67,17 +67,17 @@ export function createGetProjectDetailsQuery(options: CreateOpenedProjectQueryOp
} }
if (isLocal) { if (isLocal) {
if (state.data?.state.type === backendModule.ProjectState.opened) { if (state.data?.state.type === backendModule.ProjectState.opened) {
return openedIntervalMS return OPENED_INTERVAL_MS
} else { } else {
return activeSyncIntervalMS return ACTIVE_SYNC_INTERVAL_MS
} }
} else { } else {
if (state.data == null) { if (state.data == null) {
return activeSyncIntervalMS return ACTIVE_SYNC_INTERVAL_MS
} else if (states.includes(state.data.state.type)) { } else if (states.includes(state.data.state.type)) {
return openedIntervalMS return OPENED_INTERVAL_MS
} else { } else {
return cloudOpeningIntervalMS return CLOUD_OPENING_INTERVAL_MS
} }
} }
}, },
@ -93,8 +93,9 @@ export function createGetProjectDetailsQuery(options: CreateOpenedProjectQueryOp
} }
createGetProjectDetailsQuery.getQueryKey = (id: LaunchedProjectId) => ['project', id] as const createGetProjectDetailsQuery.getQueryKey = (id: LaunchedProjectId) => ['project', id] as const
createGetProjectDetailsQuery.createPassiveListener = (id: LaunchedProjectId) => createGetProjectDetailsQuery.createPassiveListener = (id: LaunchedProjectId) =>
reactQuery.queryOptions<backendModule.Project>({ reactQuery.queryOptions<backendModule.Project | null>({
queryKey: createGetProjectDetailsQuery.getQueryKey(id), queryKey: createGetProjectDetailsQuery.getQueryKey(id),
initialData: null,
}) })
// ============================== // ==============================

View File

@ -149,7 +149,7 @@ export default function DriveBar(props: DriveBarProps) {
}) })
}, [isCloud, doCreateDirectory, doCreateProject, inputBindings]) }, [isCloud, doCreateDirectory, doCreateProject, inputBindings])
const createdProjectQuery = useQuery<Project>( const createdProjectQuery = useQuery<Project | null>(
createdProjectId ? createdProjectId ?
createGetProjectDetailsQuery.createPassiveListener(createdProjectId) createGetProjectDetailsQuery.createPassiveListener(createdProjectId)
: { queryKey: ['__IGNORE__'], queryFn: skipToken }, : { queryKey: ['__IGNORE__'], queryFn: skipToken },

View File

@ -207,14 +207,13 @@ export function Tab(props: InternalTabProps) {
} }
}, [actuallyActive, id, setSelectedTab]) }, [actuallyActive, id, setSelectedTab])
const { isLoading, data } = reactQuery.useQuery<backend.Project>( const { isLoading, data } = reactQuery.useQuery<backend.Project | null>(
project?.id ? project?.id ?
projectHooks.createGetProjectDetailsQuery.createPassiveListener(project.id) projectHooks.createGetProjectDetailsQuery.createPassiveListener(project.id)
: { queryKey: ['__IGNORE__'], queryFn: reactQuery.skipToken }, : { queryKey: ['__IGNORE__'], queryFn: reactQuery.skipToken },
) )
const isFetching = const isFetching = isLoading || data == null || data.state.type !== backend.ProjectState.opened
(isLoading || (data && data.state.type !== backend.ProjectState.opened)) ?? false
React.useEffect(() => { React.useEffect(() => {
if (!isFetching && isLoadingRef.current) { if (!isFetching && isLoadingRef.current) {