chore: upgrade tanstack/react-query to 5.77.0 (#18039)

This commit is contained in:
Bruno Quaresma
2025-05-26 16:16:55 -03:00
committed by GitHub
parent 2a15aa8a6f
commit f3311400d1
98 changed files with 523 additions and 577 deletions
+2 -15
View File
@@ -28,7 +28,7 @@ import { DecoratorHelpers } from "@storybook/addon-themes";
import isChromatic from "chromatic/isChromatic";
import { StrictMode } from "react";
import { HelmetProvider } from "react-helmet-async";
import { QueryClient, QueryClientProvider, parseQueryArgs } from "react-query";
import { QueryClient, QueryClientProvider } from "react-query";
import { withRouter } from "storybook-addon-remix-react-router";
import "theme/globalFonts";
import themes from "../src/theme";
@@ -114,20 +114,7 @@ function withQuery(Story, { parameters }) {
if (parameters.queries) {
for (const query of parameters.queries) {
if (query.isError) {
// Based on `setQueryData`, but modified to set the result as an error.
const cache = queryClient.getQueryCache();
const parsedOptions = parseQueryArgs(query.key);
const defaultedOptions = queryClient.defaultQueryOptions(parsedOptions);
// Adds an uninitialized response to the cache, which we can now mutate.
const cachedQuery = cache.build(queryClient, defaultedOptions);
// Setting `manual` prevents retries.
cachedQuery.setData(undefined, { manual: true });
// Set the `error` value and the appropriate status.
cachedQuery.setState({ error: query.data, status: "error" });
} else {
queryClient.setQueryData(query.key, query.data);
}
queryClient.setQueryData(query.key, query.data);
}
}
+2 -2
View File
@@ -69,7 +69,7 @@
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-switch": "1.1.1",
"@radix-ui/react-tooltip": "1.1.7",
"@tanstack/react-query-devtools": "4.35.3",
"@tanstack/react-query-devtools": "5.77.0",
"@xterm/addon-canvas": "0.7.0",
"@xterm/addon-fit": "0.10.0",
"@xterm/addon-unicode11": "0.8.0",
@@ -103,7 +103,7 @@
"react-dom": "18.3.1",
"react-helmet-async": "2.0.5",
"react-markdown": "9.0.3",
"react-query": "npm:@tanstack/react-query@4.35.3",
"react-query": "npm:@tanstack/react-query@5.77.0",
"react-router-dom": "6.26.2",
"react-syntax-highlighter": "15.6.1",
"react-virtualized-auto-sizer": "1.0.24",
+23 -75
View File
@@ -119,8 +119,8 @@ importers:
specifier: 1.1.7
version: 1.1.7(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-query-devtools':
specifier: 4.35.3
version: 4.35.3(@tanstack/react-query@4.35.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
specifier: 5.77.0
version: 5.77.0(@tanstack/react-query@5.77.0(react@18.3.1))(react@18.3.1)
'@xterm/addon-canvas':
specifier: 0.7.0
version: 0.7.0(@xterm/xterm@5.5.0)
@@ -221,8 +221,8 @@ importers:
specifier: 9.0.3
version: 9.0.3(@types/react@18.3.12)(react@18.3.1)
react-query:
specifier: npm:@tanstack/react-query@4.35.3
version: '@tanstack/react-query@4.35.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
specifier: npm:@tanstack/react-query@5.77.0
version: '@tanstack/react-query@5.77.0(react@18.3.1)'
react-router-dom:
specifier: 6.26.2
version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -2398,31 +2398,22 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
'@tanstack/match-sorter-utils@8.8.4':
resolution: {integrity: sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==, tarball: https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz}
engines: {node: '>=12'}
'@tanstack/query-core@5.77.0':
resolution: {integrity: sha512-PFeWjgMQjOsnxBwnW/TJoO0pCja2dzuMQoZ3Diho7dPz7FnTUwTrjNmdf08evrhSE5nvPIKeqV6R0fvQfmhGeg==, tarball: https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.77.0.tgz}
'@tanstack/query-core@4.35.3':
resolution: {integrity: sha512-PS+WEjd9wzKTyNjjQymvcOe1yg8f3wYc6mD+vb6CKyZAKvu4sIJwryfqfBULITKCla7P9C4l5e9RXePHvZOZeQ==, tarball: https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.35.3.tgz}
'@tanstack/query-devtools@5.76.0':
resolution: {integrity: sha512-1p92nqOBPYVqVDU0Ua5nzHenC6EGZNrLnB2OZphYw8CNA1exuvI97FVgIKON7Uug3uQqvH/QY8suUKpQo8qHNQ==, tarball: https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.76.0.tgz}
'@tanstack/react-query-devtools@4.35.3':
resolution: {integrity: sha512-UvLT7qPzCuCZ3NfjwsOqDUVN84JvSOuW6ukrjZmSqgjPqVxD6ra/HUp1CEOatQY2TRvKCp8y1lTVu+trXM30fg==, tarball: https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.35.3.tgz}
'@tanstack/react-query-devtools@5.77.0':
resolution: {integrity: sha512-Dwvs+ksXiK1tW4YnTtHwYPO5+d8IUk1l8QQJ4aGEIqKz6uTLu/67NIo7EnUF0G/Edv+UOn9P1V3tYWuVfvhbmg==, tarball: https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.77.0.tgz}
peerDependencies:
'@tanstack/react-query': ^4.35.3
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
'@tanstack/react-query': ^5.77.0
react: ^18 || ^19
'@tanstack/react-query@4.35.3':
resolution: {integrity: sha512-UgTPioip/rGG3EQilXfA2j4BJkhEQsR+KAbF+KIuvQ7j4MkgnTCJF01SfRpIRNtQTlEfz/+IL7+jP8WA8bFbsw==, tarball: https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.35.3.tgz}
'@tanstack/react-query@5.77.0':
resolution: {integrity: sha512-jX52ot8WxWzWnAknpRSEWj6PTR/7nkULOfoiaVPk6nKu0otwt30UMBC9PTg/m1x0uhz1g71/imwjViTm/oYHxA==, tarball: https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.77.0.tgz}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
react: ^18 || ^19
'@testing-library/dom@10.4.0':
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==, tarball: https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz}
@@ -3258,10 +3249,6 @@ packages:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==, tarball: https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz}
engines: {node: '>= 0.6'}
copy-anything@3.0.5:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==, tarball: https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz}
engines: {node: '>=12.13'}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, tarball: https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz}
@@ -3635,6 +3622,7 @@ packages:
eslint@8.52.0:
resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==, tarball: https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
espree@9.6.1:
@@ -4219,10 +4207,6 @@ packages:
is-weakset@2.0.2:
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==, tarball: https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz}
is-what@4.1.16:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==, tarball: https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz}
engines: {node: '>=12.13'}
is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==, tarball: https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz}
engines: {node: '>=8'}
@@ -5516,9 +5500,6 @@ packages:
remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, tarball: https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz}
remove-accents@0.4.2:
resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==, tarball: https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, tarball: https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz}
engines: {node: '>=0.10.0'}
@@ -5835,10 +5816,6 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
superjson@1.13.3:
resolution: {integrity: sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==, tarball: https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz}
engines: {node: '>=10'}
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz}
engines: {node: '>=4'}
@@ -6152,11 +6129,6 @@ packages:
'@types/react':
optional: true
use-sync-external-store@1.2.0:
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==, tarball: https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
use-sync-external-store@1.4.0:
resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==, tarball: https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz}
peerDependencies:
@@ -8523,28 +8495,20 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.17(ts-node@10.9.2(@swc/core@1.3.38)(@types/node@20.17.16)(typescript@5.6.3))
'@tanstack/match-sorter-utils@8.8.4':
dependencies:
remove-accents: 0.4.2
'@tanstack/query-core@5.77.0': {}
'@tanstack/query-core@4.35.3': {}
'@tanstack/query-devtools@5.76.0': {}
'@tanstack/react-query-devtools@4.35.3(@tanstack/react-query@4.35.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
'@tanstack/react-query-devtools@5.77.0(@tanstack/react-query@5.77.0(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/match-sorter-utils': 8.8.4
'@tanstack/react-query': 4.35.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/query-devtools': 5.76.0
'@tanstack/react-query': 5.77.0(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
superjson: 1.13.3
use-sync-external-store: 1.2.0(react@18.3.1)
'@tanstack/react-query@4.35.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
'@tanstack/react-query@5.77.0(react@18.3.1)':
dependencies:
'@tanstack/query-core': 4.35.3
'@tanstack/query-core': 5.77.0
react: 18.3.1
use-sync-external-store: 1.2.0(react@18.3.1)
optionalDependencies:
react-dom: 18.3.1(react@18.3.1)
'@testing-library/dom@10.4.0':
dependencies:
@@ -9447,10 +9411,6 @@ snapshots:
cookie@0.7.2: {}
copy-anything@3.0.5:
dependencies:
is-what: 4.1.16
core-util-is@1.0.3: {}
cosmiconfig@7.1.0:
@@ -10542,8 +10502,6 @@ snapshots:
call-bind: 1.0.8
get-intrinsic: 1.3.0
is-what@4.1.16: {}
is-wsl@2.2.0:
dependencies:
is-docker: 2.2.1
@@ -12425,8 +12383,6 @@ snapshots:
mdast-util-to-markdown: 2.1.0
unified: 11.0.5
remove-accents@0.4.2: {}
require-directory@2.1.1: {}
requires-port@1.0.0: {}
@@ -12767,10 +12723,6 @@ snapshots:
pirates: 4.0.6
ts-interface-checker: 0.1.13
superjson@1.13.3:
dependencies:
copy-anything: 3.0.5
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
@@ -13108,10 +13060,6 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.12
use-sync-external-store@1.2.0(react@18.3.1):
dependencies:
react: 18.3.1
use-sync-external-store@1.4.0(react@18.3.1):
dependencies:
react: 18.3.1
+1 -1
View File
@@ -5,7 +5,7 @@ export const createChat = (queryClient: QueryClient) => {
return {
mutationFn: API.createChat,
onSuccess: async () => {
await queryClient.invalidateQueries(["chats"]);
await queryClient.invalidateQueries({ queryKey: ["chats"] });
},
};
};
+2 -2
View File
@@ -13,7 +13,7 @@ export const health = () => ({
export const refreshHealth = (queryClient: QueryClient) => {
return {
mutationFn: async () => {
await queryClient.cancelQueries(HEALTH_QUERY_KEY);
await queryClient.cancelQueries({ queryKey: HEALTH_QUERY_KEY });
const newHealthData = await API.getHealth(true);
queryClient.setQueryData(HEALTH_QUERY_KEY, newHealthData);
},
@@ -38,7 +38,7 @@ export const updateHealthSettings = (
return {
mutationFn: API.updateHealthSettings,
onSuccess: async (_, newSettings) => {
await queryClient.invalidateQueries(HEALTH_QUERY_KEY);
await queryClient.invalidateQueries({ queryKey: HEALTH_QUERY_KEY });
queryClient.setQueryData(HEALTH_QUERY_SETTINGS_KEY, newSettings);
},
};
+6 -2
View File
@@ -37,7 +37,9 @@ export const exchangeExternalAuthDevice = (
queryKey: ["external-auth", providerId, "device", deviceCode],
onSuccess: async () => {
// Force a refresh of the Git auth status.
await queryClient.invalidateQueries(["external-auth", providerId]);
await queryClient.invalidateQueries({
queryKey: ["external-auth", providerId],
});
},
};
};
@@ -57,7 +59,9 @@ export const unlinkExternalAuths = (queryClient: QueryClient) => {
return {
mutationFn: API.unlinkExternalAuthProvider,
onSuccess: async () => {
await queryClient.invalidateQueries(["external-auth"]);
await queryClient.invalidateQueries({
queryKey: ["external-auth"],
});
},
};
};
+13 -9
View File
@@ -117,10 +117,12 @@ export const createGroup = (queryClient: QueryClient, organization: string) => {
mutationFn: (request: CreateGroupRequest) =>
API.createGroup(organization, request),
onSuccess: async () => {
await queryClient.invalidateQueries(groupsQueryKey);
await queryClient.invalidateQueries(
getGroupsByOrganizationQueryKey(organization),
);
await queryClient.invalidateQueries({
queryKey: groupsQueryKey,
});
await queryClient.invalidateQueries({
queryKey: getGroupsByOrganizationQueryKey(organization),
});
},
};
};
@@ -169,11 +171,13 @@ const invalidateGroup = (
groupId: string,
) =>
Promise.all([
queryClient.invalidateQueries(groupsQueryKey),
queryClient.invalidateQueries(
getGroupsByOrganizationQueryKey(organization),
),
queryClient.invalidateQueries(getGroupQueryKey(organization, groupId)),
queryClient.invalidateQueries({ queryKey: groupsQueryKey }),
queryClient.invalidateQueries({
queryKey: getGroupsByOrganizationQueryKey(organization),
}),
queryClient.invalidateQueries({
queryKey: getGroupQueryKey(organization, groupId),
}),
]);
function sortGroupsByName<T extends Group>(
+3 -1
View File
@@ -9,7 +9,9 @@ export const patchOrganizationSyncSettings = (queryClient: QueryClient) => {
mutationFn: (request: OrganizationSyncSettings) =>
API.patchOrganizationIdpSyncSettings(request),
onSuccess: async () =>
await queryClient.invalidateQueries(getOrganizationIdpSyncSettingsKey()),
await queryClient.invalidateQueries({
queryKey: getOrganizationIdpSyncSettingsKey(),
}),
};
};
+33 -29
View File
@@ -22,7 +22,7 @@ import {
type WorkspacePermissions,
workspacePermissionChecks,
} from "modules/permissions/workspaces";
import type { QueryClient } from "react-query";
import type { QueryClient, UseQueryOptions } from "react-query";
import { meKey } from "./users";
export const createOrganization = (queryClient: QueryClient) => {
@@ -31,8 +31,8 @@ export const createOrganization = (queryClient: QueryClient) => {
API.createOrganization(params),
onSuccess: async () => {
await queryClient.invalidateQueries(meKey);
await queryClient.invalidateQueries(organizationsKey);
await queryClient.invalidateQueries({ queryKey: meKey });
await queryClient.invalidateQueries({ queryKey: organizationsKey });
},
};
};
@@ -48,7 +48,7 @@ export const updateOrganization = (queryClient: QueryClient) => {
API.updateOrganization(variables.organizationId, variables.req),
onSuccess: async () => {
await queryClient.invalidateQueries(organizationsKey);
await queryClient.invalidateQueries({ queryKey: organizationsKey });
},
};
};
@@ -59,8 +59,8 @@ export const deleteOrganization = (queryClient: QueryClient) => {
API.deleteOrganization(organizationId),
onSuccess: async () => {
await queryClient.invalidateQueries(meKey);
await queryClient.invalidateQueries(organizationsKey);
await queryClient.invalidateQueries({ queryKey: meKey });
await queryClient.invalidateQueries({ queryKey: organizationsKey });
},
};
};
@@ -117,7 +117,9 @@ export const addOrganizationMember = (queryClient: QueryClient, id: string) => {
},
onSuccess: async () => {
await queryClient.invalidateQueries(["organization", id, "members"]);
await queryClient.invalidateQueries({
queryKey: ["organization", id, "members"],
});
},
};
};
@@ -132,7 +134,9 @@ export const removeOrganizationMember = (
},
onSuccess: async () => {
await queryClient.invalidateQueries(["organization", id, "members"]);
await queryClient.invalidateQueries({
queryKey: ["organization", id, "members"],
});
},
};
};
@@ -147,11 +151,9 @@ export const updateOrganizationMemberRoles = (
},
onSuccess: async () => {
await queryClient.invalidateQueries([
"organization",
organizationId,
"members",
]);
await queryClient.invalidateQueries({
queryKey: ["organization", organizationId, "members"],
});
},
};
};
@@ -240,9 +242,9 @@ export const patchRoleSyncSettings = (
mutationFn: (request: RoleSyncSettings) =>
API.patchRoleIdpSyncSettings(request, organization),
onSuccess: async () =>
await queryClient.invalidateQueries(
getRoleIdpSyncSettingsKey(organization),
),
await queryClient.invalidateQueries({
queryKey: getRoleIdpSyncSettingsKey(organization),
}),
};
};
@@ -269,12 +271,13 @@ export const provisionerJobs = (
export const organizationsPermissions = (
organizationIds: string[] | undefined,
) => {
if (!organizationIds) {
return { enabled: false };
}
return {
queryKey: ["organizations", [...organizationIds.sort()], "permissions"],
enabled: !!organizationIds,
queryKey: [
"organizations",
[...(organizationIds ?? []).sort()],
"permissions",
],
queryFn: async () => {
// Only request what we need for the sidebar, which is one edit permission
// per sub-link (settings, groups, roles, and members pages) that tells us
@@ -283,7 +286,7 @@ export const organizationsPermissions = (
// The endpoint takes a flat array, so to avoid collisions prepend each
// check with the org ID (the key can be anything we want).
const prefixedChecks = organizationIds.flatMap((orgId) =>
const prefixedChecks = (organizationIds ?? []).flatMap((orgId) =>
Object.entries(organizationPermissionChecks(orgId)).map(
([key, val]) => [`${orgId}.${key}`, val],
),
@@ -315,14 +318,15 @@ export const workspacePermissionsByOrganization = (
organizationIds: string[] | undefined,
userId: string,
) => {
if (!organizationIds) {
return { enabled: false };
}
return {
queryKey: ["workspaces", [...organizationIds.sort()], "permissions"],
enabled: !!organizationIds,
queryKey: [
"workspaces",
[...(organizationIds ?? []).sort()],
"permissions",
],
queryFn: async () => {
const prefixedChecks = organizationIds.flatMap((orgId) =>
const prefixedChecks = (organizationIds ?? []).flatMap((orgId) =>
Object.entries(workspacePermissionChecks(orgId, userId)).map(
([key, val]) => [`${orgId}.${key}`, val],
),
@@ -346,7 +350,7 @@ export const workspacePermissionsByOrganization = (
{} as Record<string, Partial<WorkspacePermissions>>,
) as Record<string, WorkspacePermissions>;
},
};
} satisfies UseQueryOptions<Record<string, WorkspacePermissions>>;
};
const getOrganizationIdpSyncClaimFieldValuesKey = (
+9 -9
View File
@@ -33,9 +33,9 @@ export const createOrganizationRole = (
mutationFn: (request: Role) =>
API.createOrganizationRole(organization, request),
onSuccess: async (updatedRole: Role) =>
await queryClient.invalidateQueries(
getRoleQueryKey(organization, updatedRole.name),
),
await queryClient.invalidateQueries({
queryKey: getRoleQueryKey(organization, updatedRole.name),
}),
};
};
@@ -47,9 +47,9 @@ export const updateOrganizationRole = (
mutationFn: (request: Role) =>
API.updateOrganizationRole(organization, request),
onSuccess: async (updatedRole: Role) =>
await queryClient.invalidateQueries(
getRoleQueryKey(organization, updatedRole.name),
),
await queryClient.invalidateQueries({
queryKey: getRoleQueryKey(organization, updatedRole.name),
}),
};
};
@@ -61,8 +61,8 @@ export const deleteOrganizationRole = (
mutationFn: (roleName: string) =>
API.deleteOrganizationRole(organization, roleName),
onSuccess: async (_: unknown, roleName: string) =>
await queryClient.invalidateQueries(
getRoleQueryKey(organization, roleName),
),
await queryClient.invalidateQueries({
queryKey: getRoleQueryKey(organization, roleName),
}),
};
};
+5 -5
View File
@@ -11,13 +11,11 @@ const userQuietHoursScheduleKey = (userId: string) => [
"quietHours",
];
export const userQuietHoursSchedule = (
userId: string,
): QueryOptions<UserQuietHoursScheduleResponse> => {
export const userQuietHoursSchedule = (userId: string) => {
return {
queryKey: userQuietHoursScheduleKey(userId),
queryFn: () => API.getUserQuietHoursSchedule(userId),
};
} satisfies QueryOptions<UserQuietHoursScheduleResponse>;
};
export const updateUserQuietHoursSchedule = (
@@ -28,7 +26,9 @@ export const updateUserQuietHoursSchedule = (
mutationFn: (request: UpdateUserQuietHoursScheduleRequest) =>
API.updateUserQuietHoursSchedule(userId, request),
onSuccess: async () => {
await queryClient.invalidateQueries(userQuietHoursScheduleKey(userId));
await queryClient.invalidateQueries({
queryKey: userQuietHoursScheduleKey(userId),
});
},
};
};
+13 -12
View File
@@ -15,11 +15,11 @@ import { getTemplateVersionFiles } from "utils/templateVersion";
const templateKey = (templateId: string) => ["template", templateId];
export const template = (templateId: string): QueryOptions<Template> => {
export const template = (templateId: string) => {
return {
queryKey: templateKey(templateId),
queryFn: async () => API.getTemplate(templateId),
};
} satisfies QueryOptions<Template>;
};
export const templateByNameKey = (organization: string, name: string) => [
@@ -28,14 +28,11 @@ export const templateByNameKey = (organization: string, name: string) => [
name,
];
export const templateByName = (
organization: string,
name: string,
): QueryOptions<Template> => {
export const templateByName = (organization: string, name: string) => {
return {
queryKey: templateByNameKey(organization, name),
queryFn: async () => API.getTemplateByName(organization, name),
};
} satisfies QueryOptions<Template>;
};
const getTemplatesQueryKey = (
@@ -88,7 +85,9 @@ export const setUserRole = (
},
}),
onSuccess: async (_res, { templateId }) => {
await queryClient.invalidateQueries(["templateAcl", templateId]);
await queryClient.invalidateQueries({
queryKey: ["templateAcl", templateId],
});
},
};
};
@@ -108,7 +107,9 @@ export const setGroupRole = (
},
}),
onSuccess: async (_res, { templateId }) => {
await queryClient.invalidateQueries(["templateAcl", templateId]);
await queryClient.invalidateQueries({
queryKey: ["templateAcl", templateId],
});
},
};
};
@@ -197,9 +198,9 @@ export const updateActiveTemplateVersion = (
}),
onSuccess: async () => {
// invalidated because of `active_version_id`
await queryClient.invalidateQueries(
templateByNameKey(template.organization_id, template.name),
);
await queryClient.invalidateQueries({
queryKey: templateByNameKey(template.organization_id, template.name),
});
},
};
};
+9 -7
View File
@@ -51,7 +51,7 @@ export const users = (req: UsersRequest): UseQueryOptions<GetUsersResponse> => {
return {
queryKey: usersKey(req),
queryFn: ({ signal }) => API.getUsers(req, signal),
cacheTime: 5 * 1000 * 60,
gcTime: 5 * 1000 * 60,
};
};
@@ -69,7 +69,7 @@ export const createUser = (queryClient: QueryClient) => {
return {
mutationFn: API.createUser,
onSuccess: async () => {
await queryClient.invalidateQueries(["users"]);
await queryClient.invalidateQueries({ queryKey: ["users"] });
},
};
};
@@ -84,7 +84,7 @@ export const suspendUser = (queryClient: QueryClient) => {
return {
mutationFn: API.suspendUser,
onSuccess: async () => {
await queryClient.invalidateQueries(["users"]);
await queryClient.invalidateQueries({ queryKey: ["users"] });
},
};
};
@@ -93,7 +93,7 @@ export const activateUser = (queryClient: QueryClient) => {
return {
mutationFn: API.activateUser,
onSuccess: async () => {
await queryClient.invalidateQueries(["users"]);
await queryClient.invalidateQueries({ queryKey: ["users"] });
},
};
};
@@ -102,7 +102,7 @@ export const deleteUser = (queryClient: QueryClient) => {
return {
mutationFn: API.deleteUser,
onSuccess: async () => {
await queryClient.invalidateQueries(["users"]);
await queryClient.invalidateQueries({ queryKey: ["users"] });
},
};
};
@@ -112,7 +112,7 @@ export const updateRoles = (queryClient: QueryClient) => {
mutationFn: ({ userId, roles }: { userId: string; roles: string[] }) =>
API.updateUserRoles(roles, userId),
onSuccess: async () => {
await queryClient.invalidateQueries(["users"]);
await queryClient.invalidateQueries({ queryKey: ["users"] });
},
};
};
@@ -257,7 +257,9 @@ export const updateAppearanceSettings = (
onSuccess: async () =>
// Could technically invalidate more, but we only ever care about the
// `theme_preference` for the `me` query.
await queryClient.invalidateQueries(myAppearanceKey),
await queryClient.invalidateQueries({
queryKey: myAppearanceKey,
}),
};
};
+2 -2
View File
@@ -2,12 +2,12 @@ import type { MetadataState, MetadataValue } from "hooks/useEmbeddedMetadata";
import type { QueryKey, UseQueryOptions } from "react-query";
export const disabledRefetchOptions = {
cacheTime: Number.POSITIVE_INFINITY,
gcTime: Number.POSITIVE_INFINITY,
staleTime: Number.POSITIVE_INFINITY,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
} as const satisfies UseQueryOptions;
} as const satisfies Partial<UseQueryOptions>;
type UseQueryOptionsWithMetadata<
TMetadata extends MetadataValue = MetadataValue,
+7 -3
View File
@@ -37,7 +37,7 @@ export const workspaceBuildsKey = (workspaceId: string) => [
export const infiniteWorkspaceBuilds = (
workspaceId: string,
req?: WorkspaceBuildsRequest,
): UseInfiniteQueryOptions<WorkspaceBuild[]> => {
) => {
const limit = req?.limit ?? 25;
return {
@@ -48,13 +48,17 @@ export const infiniteWorkspaceBuilds = (
}
return pages.length + 1;
},
queryFn: ({ pageParam = 0 }) => {
initialPageParam: 0,
queryFn: ({ pageParam }) => {
if (typeof pageParam !== "number") {
throw new Error("pageParam must be a number");
}
return API.getWorkspaceBuilds(workspaceId, {
limit,
offset: pageParam <= 0 ? 0 : (pageParam - 1) * limit,
});
},
};
} satisfies UseInfiniteQueryOptions<WorkspaceBuild[]>;
};
// We use readyAgentsCount to invalidate the query when an agent connects
+2 -2
View File
@@ -55,7 +55,7 @@ export const createWorkspace = (queryClient: QueryClient) => {
return API.createWorkspace(userId, req);
},
onSuccess: async () => {
await queryClient.invalidateQueries(["workspaces"]);
await queryClient.invalidateQueries({ queryKey: ["workspaces"] });
},
};
};
@@ -114,7 +114,7 @@ export const autoCreateWorkspace = (queryClient: QueryClient) => {
});
},
onSuccess: async () => {
await queryClient.invalidateQueries(["workspaces"]);
await queryClient.invalidateQueries({ queryKey: ["workspaces"] });
},
};
};
+2 -2
View File
@@ -1,6 +1,6 @@
import type { SelectFilterOption } from "components/Filter/SelectFilter";
import { useMemo, useRef, useState } from "react";
import { useQuery } from "react-query";
import { keepPreviousData, useQuery } from "react-query";
export type UseFilterMenuOptions = {
id: string;
@@ -40,7 +40,7 @@ export const useFilterMenu = ({
return getSelectedOption();
},
enabled,
keepPreviousData: true,
placeholderData: keepPreviousData,
});
const selectedOption = selectedOptionQuery.data;
const searchOptionsQuery = useQuery({
@@ -29,24 +29,26 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
}) => {
const [open, setOpen] = useState(false);
const [selected, setSelected] = useState<Organization | null>(null);
const organizationsQuery = useQuery(organizations());
const checks =
check &&
organizationsQuery.data &&
Object.fromEntries(
organizationsQuery.data.map((org) => [
org.id,
{
...check,
object: { ...check.object, organization_id: org.id },
},
]),
);
const permissionsQuery = useQuery(
check && organizationsQuery.data
? checkAuthorization({
checks: Object.fromEntries(
organizationsQuery.data.map((org) => [
org.id,
{
...check,
object: { ...check.object, organization_id: org.id },
},
]),
),
})
: { enabled: false },
);
const permissionsQuery = useQuery({
...checkAuthorization({
checks: checks ?? {},
}),
enabled: Boolean(check && organizationsQuery.data),
});
// If an authorization check was provided, filter the organizations based on
// the results of that check.
@@ -9,7 +9,7 @@ import type { PaginationResult } from "./PaginationContainer";
type ResultBase = Omit<
PaginationResult,
"isPreviousData" | "currentOffsetStart" | "totalRecords" | "totalPages"
"isPlaceholderData" | "currentOffsetStart" | "totalRecords" | "totalPages"
>;
export const mockPaginationResultBase: ResultBase = {
@@ -27,7 +27,7 @@ export const mockPaginationResultBase: ResultBase = {
export const mockInitialRenderResult: PaginationResult = {
...mockPaginationResultBase,
isSuccess: false,
isPreviousData: false,
isPlaceholderData: false,
currentOffsetStart: undefined,
hasNextPage: false,
hasPreviousPage: false,
@@ -38,7 +38,7 @@ export const mockInitialRenderResult: PaginationResult = {
export const mockSuccessResult: PaginationResult = {
...mockPaginationResultBase,
isSuccess: true,
isPreviousData: false,
isPlaceholderData: false,
currentOffsetStart: 1,
totalPages: 1,
totalRecords: 4,
@@ -49,7 +49,7 @@ export const FirstPageWithData: Story = {
totalPages: 4,
hasPreviousPage: false,
hasNextPage: true,
isPreviousData: false,
isPlaceholderData: false,
},
},
};
@@ -65,7 +65,7 @@ export const FirstPageWithLittleData: Story = {
totalPages: 1,
hasPreviousPage: false,
hasNextPage: false,
isPreviousData: false,
isPlaceholderData: false,
},
},
};
@@ -81,7 +81,7 @@ export const FirstPageWithNoData: Story = {
totalPages: 0,
hasPreviousPage: false,
hasNextPage: false,
isPreviousData: false,
isPlaceholderData: false,
},
},
};
@@ -97,7 +97,7 @@ export const TransitionFromFirstToSecondPage: Story = {
totalPages: 4,
hasPreviousPage: false,
hasNextPage: false,
isPreviousData: true,
isPlaceholderData: true,
},
children: <div>Previous data from page 1</div>,
},
@@ -114,7 +114,7 @@ export const SecondPageWithData: Story = {
totalPages: 4,
hasPreviousPage: true,
hasNextPage: true,
isPreviousData: false,
isPlaceholderData: false,
},
children: <div>New data for page 2</div>,
},
@@ -4,7 +4,7 @@ import { PaginationHeader } from "./PaginationHeader";
import { PaginationWidgetBase } from "./PaginationWidgetBase";
export type PaginationResult = PaginationResultInfo & {
isPreviousData: boolean;
isPlaceholderData: boolean;
};
type PaginationProps = HTMLAttributes<HTMLDivElement> & {
@@ -2,7 +2,7 @@ import TextField, { type TextFieldProps } from "@mui/material/TextField";
import { API } from "api/api";
import { useDebouncedValue } from "hooks/debounce";
import type { FC } from "react";
import { useQuery } from "react-query";
import { keepPreviousData, useQuery } from "react-query";
// TODO: @BrunoQuaresma: Unable to integrate Yup + Formik for validation. The
// validation was triggering on the onChange event, but the form.errors were not
@@ -19,7 +19,7 @@ export const PasswordField: FC<TextFieldProps> = (props) => {
const validatePasswordQuery = useQuery({
queryKey: ["validatePassword", debouncedValue],
queryFn: () => API.validateUserPassword(debouncedValue),
keepPreviousData: true,
placeholderData: keepPreviousData,
enabled: debouncedValue.length > 0,
});
const valid = validatePasswordQuery.data?.valid ?? true;
@@ -15,7 +15,7 @@ import {
type FC,
useState,
} from "react";
import { useQuery } from "react-query";
import { keepPreviousData, useQuery } from "react-query";
import { prepareQuery } from "utils/filters";
// The common properties between users and org members that we need.
@@ -45,7 +45,7 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = (props) => {
limit: 25,
}),
enabled: filter !== undefined,
keepPreviousData: true,
placeholderData: keepPreviousData,
});
return (
<InnerAutocomplete<User>
@@ -72,7 +72,7 @@ export const MemberAutocomplete: FC<MemberAutocompleteProps> = ({
const membersQuery = useQuery({
...organizationMembers(organizationId),
enabled: filter !== undefined,
keepPreviousData: true,
placeholderData: keepPreviousData,
});
return (
<InnerAutocomplete<OrganizationMemberWithUserData>
+3 -3
View File
@@ -72,7 +72,7 @@ export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
userQuery.isError &&
isApiError(userQuery.error) &&
userQuery.error.response.status === 401;
const isSigningOut = logoutMutation.isLoading;
const isSigningOut = logoutMutation.isPending;
const isLoading =
userQuery.isLoading ||
hasFirstUserQuery.isLoading ||
@@ -80,8 +80,8 @@ export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
const isConfiguringTheFirstUser =
!hasFirstUserQuery.isLoading && !hasFirstUserQuery.data;
const isSignedIn = userQuery.isSuccess && userQuery.data !== undefined;
const isSigningIn = loginMutation.isLoading;
const isUpdatingProfile = updateProfileMutation.isLoading;
const isSigningIn = loginMutation.isPending;
const isUpdatingProfile = updateProfileMutation.isPending;
const signOut = useCallback(() => {
logoutMutation.mutate();
+2 -1
View File
@@ -5,6 +5,7 @@ import {
type QueryKey,
type UseQueryOptions,
type UseQueryResult,
keepPreviousData,
useQuery,
useQueryClient,
} from "react-query";
@@ -140,7 +141,7 @@ export function usePaginatedQuery<
const query = useQuery<TQueryFnData, TError, TData, TQueryKey>({
...extraOptions,
...getQueryOptionsFromPage(currentPage),
keepPreviousData: true,
placeholderData: keepPreviousData,
});
const totalRecords = query.data?.count;
@@ -47,7 +47,7 @@ export const NotificationsInbox: FC<NotificationsInboxProps> = ({
res: ListInboxNotificationsResponse,
) => ListInboxNotificationsResponse,
) => {
await queryClient.cancelQueries(NOTIFICATIONS_QUERY_KEY);
await queryClient.cancelQueries({ queryKey: NOTIFICATIONS_QUERY_KEY });
queryClient.setQueryData<ListInboxNotificationsResponse>(
NOTIFICATIONS_QUERY_KEY,
(prev) => {
@@ -90,7 +90,7 @@ export const NotificationsInbox: FC<NotificationsInboxProps> = ({
const {
mutate: loadMoreNotifications,
isLoading: isLoadingMoreNotifications,
isPending: isLoadingMoreNotifications,
} = useMutation({
mutationFn: async () => {
if (!inboxRes || inboxRes.notifications.length === 0) {
+2 -2
View File
@@ -157,8 +157,8 @@ export const AgentRow: FC<AgentRowProps> = ({
select: (res) => res.containers.filter((c) => c.status === "running"),
// TODO: Implement a websocket connection to get updates on containers
// without having to poll.
refetchInterval: (_, query) => {
const { error } = query.state;
refetchInterval: ({ state }) => {
const { error } = state;
return isAxiosError(error) && error.response?.status === 403
? false
: 10_000;
@@ -152,7 +152,7 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
// share port form
const {
mutateAsync: upsertWorkspacePortShareForm,
isLoading: isSubmitting,
isPending: isSubmitting,
error: submitError,
} = useMutation(upsertWorkspacePortShare(workspaceID));
const validationSchema = getValidationSchema();
@@ -34,11 +34,10 @@ function getDuplicationUrlParams(
export function useWorkspaceDuplication(workspace?: Workspace) {
const navigate = useNavigate();
const getLink = useLinks();
const buildParametersQuery = useQuery(
workspace !== undefined
? workspaceBuildParameters(workspace.latest_build.id)
: { enabled: false },
);
const buildParametersQuery = useQuery({
...workspaceBuildParameters(workspace?.latest_build.id ?? ""),
enabled: !!workspace,
});
// Not using useEffectEvent for this, because useEffect isn't really an
// intended use case for this custom hook
@@ -58,7 +58,7 @@ export const useWorkspaceUpdate = ({
return {
update,
isUpdating: updateWorkspaceMutation.isLoading,
isUpdating: updateWorkspaceMutation.isPending,
dialogs: {
updateConfirmation: {
open: isConfirmingUpdate,
+1 -1
View File
@@ -170,7 +170,7 @@ export const ChatLayout: FC = () => {
variant="outline"
size="sm"
onClick={handleNewChat}
disabled={createChatMutation.isLoading}
disabled={createChatMutation.isPending}
>
<PlusIcon />
New Chat
@@ -221,14 +221,10 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
});
const getFieldHelpers = getFormHelpers<CreateTemplateFormData>(form, error);
const { data: provisioners } = useQuery(
selectedOrg
? {
...provisionerDaemons(selectedOrg.id),
enabled: showOrganizationPicker,
}
: { enabled: false },
);
const { data: provisioners } = useQuery({
...provisionerDaemons(selectedOrg?.id ?? ""),
enabled: showOrganizationPicker && !!selectedOrg,
});
// TODO: Ideally, we would have a backend endpoint that could notify the
// frontend that a provisioner has been connected, so that we could hide
@@ -40,7 +40,7 @@ const CreateTemplatePage: FC = () => {
},
onOpenBuildLogsDrawer: () => setIsBuildLogsOpen(true),
error: createTemplateMutation.error,
isCreating: createTemplateMutation.isLoading,
isCreating: createTemplateMutation.isPending,
variablesSectionRef,
};
@@ -5,6 +5,7 @@ import {
templateVersionLogs,
templateVersionVariables,
} from "api/queries/templates";
import type { Template, TemplateVersion } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
@@ -25,7 +26,9 @@ export const DuplicateTemplateView: FC<CreateTemplatePageViewProps> = ({
const navigate = useNavigate();
const { entitlements } = useDashboard();
const [searchParams] = useSearchParams();
const templateQuery = useQuery(template(searchParams.get("fromTemplate")!));
const templateQuery = useQuery(
template(searchParams.get("fromTemplate") as string),
);
const activeVersionId = templateQuery.data?.active_version_id ?? "";
const templateVersionQuery = useQuery({
...templateVersion(activeVersionId),
@@ -65,7 +68,7 @@ export const DuplicateTemplateView: FC<CreateTemplatePageViewProps> = ({
{...formPermissions}
variablesSectionRef={variablesSectionRef}
onOpenBuildLogsDrawer={onOpenBuildLogsDrawer}
copiedTemplate={templateQuery.data!}
copiedTemplate={templateQuery.data as Template}
error={error}
isSubmitting={isCreating}
variables={templateVersionVariablesQuery.data}
@@ -74,9 +77,9 @@ export const DuplicateTemplateView: FC<CreateTemplatePageViewProps> = ({
logs={templateVersionLogsQuery.data}
onSubmit={async (formData) => {
await onCreateTemplate({
organization: templateQuery.data!.organization_name,
organization: (templateQuery.data as Template).organization_name,
version: firstVersionFromFile(
templateVersionQuery.data!.job.file_id,
(templateVersionQuery.data as TemplateVersion).job.file_id,
formData.user_variable_values,
formData.provisioner_type,
formData.tags,
@@ -8,7 +8,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
import { useQuery } from "react-query";
import { keepPreviousData, useQuery } from "react-query";
import { useNavigate, useSearchParams } from "react-router-dom";
import { CreateTemplateForm } from "./CreateTemplateForm";
import type { CreateTemplatePageViewProps } from "./types";
@@ -46,7 +46,7 @@ export const ImportStarterTemplateView: FC<CreateTemplatePageViewProps> = ({
const missedVariables = useQuery({
...templateVersionVariables(isJobError ? error.version.id : ""),
keepPreviousData: true,
placeholderData: keepPreviousData,
enabled:
isJobError && error.job.error_code === "REQUIRED_TEMPLATE_VARIABLES",
});
@@ -60,7 +60,7 @@ export const UploadTemplateView: FC<CreateTemplatePageViewProps> = ({
uploadFileMutation.reset();
}
},
isUploading: uploadFileMutation.isLoading,
isUploading: uploadFileMutation.isPending,
onRemove: uploadFileMutation.reset,
file: uploadFileMutation.variables,
}}
@@ -24,11 +24,11 @@ const CreateTokenPage: FC = () => {
const {
mutate: saveToken,
isLoading: isCreating,
isPending: isCreating,
isError: creationFailed,
isSuccess: creationSuccessful,
data: newToken,
} = useMutation(API.createToken);
} = useMutation({ mutationFn: API.createToken });
const {
data: tokenConfig,
isLoading: fetchingTokenConfig,
@@ -28,7 +28,7 @@ const CreateUserPage: FC = () => {
<CreateUserForm
error={createUserMutation.error}
isLoading={createUserMutation.isLoading}
isLoading={createUserMutation.isPending}
onSubmit={async (user) => {
await createUserMutation.mutateAsync({
username: user.username,
@@ -15,44 +15,33 @@ const CreateWorkspaceExperimentRouter: FC = () => {
const { organization: organizationName = "default", template: templateName } =
useParams() as { organization?: string; template: string };
const templateQuery = useQuery(
dynamicParametersEnabled
? templateByName(organizationName, templateName)
: { enabled: false },
);
const templateQuery = useQuery({
...templateByName(organizationName, templateName),
enabled: dynamicParametersEnabled,
});
const optOutQuery = useQuery(
templateQuery.data
? {
queryKey: [
organizationName,
"template",
templateQuery.data.id,
"optOut",
],
queryFn: () => {
const templateId = templateQuery.data.id;
const localStorageKey = optOutKey(templateId);
const storedOptOutString = localStorage.getItem(localStorageKey);
const optOutQuery = useQuery({
enabled: !!templateQuery.data,
queryKey: [organizationName, "template", templateQuery.data?.id, "optOut"],
queryFn: () => {
const templateId = templateQuery.data?.id;
const localStorageKey = optOutKey(templateId ?? "");
const storedOptOutString = localStorage.getItem(localStorageKey);
let optOutResult: boolean;
let optOutResult: boolean;
if (storedOptOutString !== null) {
optOutResult = storedOptOutString === "true";
} else {
optOutResult = Boolean(
templateQuery.data.use_classic_parameter_flow,
);
}
if (storedOptOutString !== null) {
optOutResult = storedOptOutString === "true";
} else {
optOutResult = !!templateQuery.data?.use_classic_parameter_flow;
}
return {
templateId: templateId,
optedOut: optOutResult,
};
},
}
: { enabled: false },
);
return {
templateId: templateId,
optedOut: optOutResult,
};
},
});
if (dynamicParametersEnabled) {
if (optOutQuery.isLoading) {
@@ -63,7 +52,7 @@ const CreateWorkspaceExperimentRouter: FC = () => {
}
const toggleOptedOut = () => {
const key = optOutKey(optOutQuery.data.templateId);
const key = optOutKey(optOutQuery.data?.templateId ?? "");
const storedValue = localStorage.getItem(key);
const current = storedValue
@@ -9,6 +9,7 @@ import {
} from "api/queries/templates";
import { autoCreateWorkspace, createWorkspace } from "api/queries/workspaces";
import type {
Template,
TemplateVersionParameter,
UserParameter,
Workspace,
@@ -62,15 +63,14 @@ const CreateWorkspacePage: FC = () => {
);
const templateVersionPresetsQuery = useQuery({
...templateVersionPresets(templateQuery.data?.active_version_id ?? ""),
enabled: templateQuery.data !== undefined,
enabled: !!templateQuery.data,
});
const permissionsQuery = useQuery({
...checkAuthorization({
checks: createWorkspaceChecks(templateQuery.data?.organization_id ?? ""),
}),
enabled: !!templateQuery.data,
});
const permissionsQuery = useQuery(
templateQuery.data
? checkAuthorization({
checks: createWorkspaceChecks(templateQuery.data.organization_id),
})
: { enabled: false },
);
const realizedVersionId =
customVersionId ?? templateQuery.data?.active_version_id;
const organizationId = templateQuery.data?.organization_id;
@@ -96,7 +96,7 @@ const CreateWorkspacePage: FC = () => {
const loadFormDataError =
templateQuery.error ?? permissionsQuery.error ?? richParametersQuery.error;
const title = autoCreateWorkspaceMutation.isLoading
const title = autoCreateWorkspaceMutation.isPending
? "Creating workspace..."
: "Create workspace";
@@ -111,7 +111,7 @@ const CreateWorkspacePage: FC = () => {
const autofillEnabled = experiments.includes("auto-fill-parameters");
const userParametersQuery = useQuery({
queryKey: ["userParameters"],
queryFn: () => API.getUserParameters(templateQuery.data!.id),
queryFn: () => API.getUserParameters(templateQuery.data?.id ?? ""),
enabled: autofillEnabled && templateQuery.isSuccess,
});
const autofillParameters = getAutofillParameters(
@@ -203,7 +203,7 @@ const CreateWorkspacePage: FC = () => {
autoCreateWorkspaceMutation.error
}
resetMutation={createWorkspaceMutation.reset}
template={templateQuery.data!}
template={templateQuery.data as Template}
versionId={realizedVersionId}
externalAuth={externalAuth ?? []}
externalAuthPollingState={externalAuthPollingState}
@@ -212,7 +212,7 @@ const CreateWorkspacePage: FC = () => {
permissions={permissionsQuery.data as CreateWorkspacePermissions}
parameters={realizedParameters as TemplateVersionParameter[]}
presets={templateVersionPresetsQuery.data ?? []}
creatingWorkspace={createWorkspaceMutation.isLoading}
creatingWorkspace={createWorkspaceMutation.isPending}
onCancel={() => {
navigate(-1);
}}
@@ -245,15 +245,11 @@ const useExternalAuth = (versionId: string | undefined) => {
setExternalAuthPollingState("polling");
}, []);
const { data: externalAuth, isLoading: isLoadingExternalAuth } = useQuery(
versionId
? {
...templateVersionExternalAuth(versionId),
refetchInterval:
externalAuthPollingState === "polling" ? 1000 : false,
}
: { enabled: false },
);
const { data: externalAuth, isPending: isLoadingExternalAuth } = useQuery({
...templateVersionExternalAuth(versionId ?? ""),
enabled: !!versionId,
refetchInterval: externalAuthPollingState === "polling" ? 1000 : false,
});
const allSignedIn = externalAuth?.every((it) => it.authenticated);
@@ -73,18 +73,16 @@ const CreateWorkspacePageExperimental: FC = () => {
const templateQuery = useQuery(
templateByName(organizationName, templateName),
);
const templateVersionPresetsQuery = useQuery(
templateQuery.data
? templateVersionPresets(templateQuery.data.active_version_id)
: { enabled: false },
);
const permissionsQuery = useQuery(
templateQuery.data
? checkAuthorization({
checks: createWorkspaceChecks(templateQuery.data.organization_id),
})
: { enabled: false },
);
const templateVersionPresetsQuery = useQuery({
...templateVersionPresets(templateQuery.data?.active_version_id ?? ""),
enabled: !!templateQuery.data,
});
const permissionsQuery = useQuery({
...checkAuthorization({
checks: createWorkspaceChecks(templateQuery.data?.organization_id ?? ""),
}),
enabled: !!templateQuery.data,
});
const realizedVersionId =
customVersionId ?? templateQuery.data?.active_version_id;
@@ -192,7 +190,7 @@ const CreateWorkspacePageExperimental: FC = () => {
permissionsQuery.isLoading;
const loadFormDataError = templateQuery.error ?? permissionsQuery.error;
const title = autoCreateWorkspaceMutation.isLoading
const title = autoCreateWorkspaceMutation.isPending
? "Creating workspace..."
: "Create workspace";
@@ -308,7 +306,7 @@ const CreateWorkspacePageExperimental: FC = () => {
permissions={permissionsQuery.data as CreateWorkspacePermissions}
parameters={sortedParams}
presets={templateVersionPresetsQuery.data ?? []}
creatingWorkspace={createWorkspaceMutation.isLoading}
creatingWorkspace={createWorkspaceMutation.isPending}
sendMessage={sendMessage}
onCancel={() => {
navigate(-1);
@@ -344,15 +342,11 @@ const useExternalAuth = (versionId: string | undefined) => {
setExternalAuthPollingState("polling");
}, []);
const { data: externalAuth, isLoading: isLoadingExternalAuth } = useQuery(
versionId
? {
...templateVersionExternalAuth(versionId),
refetchInterval:
externalAuthPollingState === "polling" ? 1000 : false,
}
: { enabled: false },
);
const { data: externalAuth, isLoading: isLoadingExternalAuth } = useQuery({
...templateVersionExternalAuth(versionId ?? ""),
enabled: !!versionId,
refetchInterval: externalAuthPollingState === "polling" ? 1000 : false,
});
const allSignedIn = externalAuth?.every((it) => it.authenticated);
@@ -27,7 +27,7 @@ const AppearanceSettingsPage: FC = () => {
try {
await updateAppearanceMutation.mutateAsync(newAppearance);
await queryClient.invalidateQueries(appearanceConfigKey);
await queryClient.invalidateQueries({ queryKey: appearanceConfigKey });
displaySuccess("Successfully updated appearance settings!");
} catch (error) {
displayError(
@@ -36,9 +36,10 @@ const IdpOrgSyncPage: FC = () => {
setField(settingsQuery.data.field);
}, [settingsQuery.data]);
const fieldValuesQuery = useQuery(
field ? deploymentIdpSyncFieldValues(field) : { enabled: false },
);
const fieldValuesQuery = useQuery({
...deploymentIdpSyncFieldValues(field),
enabled: !!field,
});
const patchOrganizationSyncSettingsMutation = useMutation(
patchOrganizationSyncSettings(queryClient),
@@ -12,9 +12,10 @@ const AddNewLicensePage: FC = () => {
const {
mutate: saveLicenseKeyApi,
isLoading: isCreating,
isPending: isCreating,
error: savingLicenseError,
} = useMutation(API.createLicense, {
} = useMutation({
mutationFn: API.createLicense,
onSuccess: () => {
displaySuccess("You have successfully added a license");
navigate("/deployment/licenses?success=true");
@@ -37,11 +37,12 @@ const LicensesSettingsPage: FC = () => {
}
}, [entitlementsQuery.error]);
const { mutate: removeLicenseApi, isLoading: isRemovingLicense } =
useMutation(API.removeLicense, {
const { mutate: removeLicenseApi, isPending: isRemovingLicense } =
useMutation({
mutationFn: API.removeLicense,
onSuccess: () => {
displaySuccess("Successfully removed license");
void queryClient.invalidateQueries(["licenses"]);
void queryClient.invalidateQueries({ queryKey: ["licenses"] });
},
onError: () => {
displayError("Failed to remove license");
@@ -77,7 +78,7 @@ const LicensesSettingsPage: FC = () => {
<LicensesSettingsPageView
showConfetti={confettiOn}
isLoading={isLoading}
isRefreshing={refreshEntitlementsMutation.isLoading}
isRefreshing={refreshEntitlementsMutation.isPending}
userLimitActual={entitlementsQuery.data?.features.user_limit.actual}
userLimitLimit={entitlementsQuery.data?.features.user_limit.limit}
licenses={licenses}
@@ -7,13 +7,11 @@ import type { FC } from "react";
import { useMutation } from "react-query";
export const Troubleshooting: FC = () => {
const { mutate: sendTestNotificationApi, isLoading } = useMutation(
API.postTestNotification,
{
onSuccess: () => displaySuccess("Test notification sent"),
onError: () => displayError("Failed to send test notification"),
},
);
const { mutate: sendTestNotificationApi, isPending } = useMutation({
mutationFn: API.postTestNotification,
onSuccess: () => displaySuccess("Test notification sent"),
onError: () => displayError("Failed to send test notification"),
});
const theme = useTheme();
return (
@@ -33,12 +31,12 @@ export const Troubleshooting: FC = () => {
<Button
variant="outline"
size="sm"
disabled={isLoading}
disabled={isPending}
onClick={() => {
sendTestNotificationApi();
}}
>
<Spinner loading={isLoading} />
<Spinner loading={isPending} />
Send notification
</Button>
</span>
@@ -19,7 +19,7 @@ const CreateOAuth2AppPage: FC = () => {
</Helmet>
<CreateOAuth2AppPageView
isUpdating={postAppMutation.isLoading}
isUpdating={postAppMutation.isPending}
error={postAppMutation.error}
createApp={async (req) => {
try {
@@ -39,10 +39,10 @@ const EditOAuth2AppPage: FC = () => {
isLoadingApp={appQuery.isLoading}
isLoadingSecrets={secretsQuery.isLoading}
mutatingResource={{
updateApp: putAppMutation.isLoading,
deleteApp: deleteAppMutation.isLoading,
createSecret: postSecretMutation.isLoading,
deleteSecret: deleteSecretMutation.isLoading,
updateApp: putAppMutation.isPending,
deleteApp: deleteAppMutation.isPending,
createSecret: postSecretMutation.isPending,
deleteSecret: deleteSecretMutation.isPending,
}}
fullNewSecret={fullNewSecret}
ackFullNewSecret={() => setFullNewSecret(undefined)}
@@ -29,7 +29,7 @@ const CreateGroupPage: FC = () => {
);
}}
error={createGroupMutation.error}
isLoading={createGroupMutation.isLoading}
isLoading={createGroupMutation.isPending}
/>
</>
);
+17 -14
View File
@@ -1,5 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import { spyOn, userEvent, within } from "@storybook/test";
import { API } from "api/api";
import { getGroupQueryKey, groupPermissionsKey } from "api/queries/groups";
import { organizationMembersKey } from "api/queries/organizations";
import { reactRouterParameters } from "storybook-addon-remix-react-router";
@@ -52,11 +53,9 @@ export const LoadingGroup: Story = {
};
export const GroupError: Story = {
parameters: {
queries: [
{ ...groupQuery(new Error("test group error")), isError: true },
permissionsQuery({}),
],
beforeEach: () => {
spyOn(API, "getGroup").mockRejectedValue(new Error("test group error"));
spyOn(API, "checkAuthorization").mockResolvedValue({});
},
};
@@ -89,16 +88,19 @@ export const EveryoneGroup: Story = {
};
export const MembersError: Story = {
parameters: {
queries: [
groupQuery(MockGroup),
permissionsQuery({ canUpdateGroup: true }),
{ ...membersQuery(new Error("test members error")), isError: true },
],
beforeEach() {
spyOn(API, "getGroup").mockResolvedValue(MockGroup);
spyOn(API, "checkAuthorization").mockResolvedValue({
canUpdateGroup: true,
});
spyOn(API, "getOrganizationPaginatedMembers").mockRejectedValue(
new Error("test members error"),
);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole("button", { name: "Open" }));
const combobox = await canvas.findByRole("combobox");
await userEvent.click(combobox);
},
};
@@ -115,7 +117,8 @@ export const NoMembers: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole("button", { name: "Open" }));
const combobox = await canvas.findByRole("combobox");
await userEvent.click(combobox);
},
};
+6 -5
View File
@@ -67,9 +67,10 @@ const GroupPage: FC = () => {
const navigate = useNavigate();
const groupQuery = useQuery(group(organization, groupName));
const groupData = groupQuery.data;
const { data: permissions } = useQuery(
groupData ? groupPermissions(groupData.id) : { enabled: false },
);
const { data: permissions } = useQuery({
...groupPermissions(groupData?.id ?? ""),
enabled: !!groupData,
});
const addMemberMutation = useMutation(addMember(queryClient));
const removeMemberMutation = useMutation(removeMember(queryClient));
const deleteGroupMutation = useMutation(deleteGroup(queryClient));
@@ -145,7 +146,7 @@ const GroupPage: FC = () => {
<Stack spacing={1}>
{canUpdateGroup && groupData && !isEveryoneGroup(groupData) && (
<AddGroupMember
isLoading={addMemberMutation.isLoading}
isLoading={addMemberMutation.isPending}
organizationId={groupData.organization_id}
onSubmit={async (member, reset) => {
try {
@@ -220,7 +221,7 @@ const GroupPage: FC = () => {
{groupQuery.data && (
<DeleteDialog
isOpen={isDeletingGroup}
confirmLoading={deleteGroupMutation.isLoading}
confirmLoading={deleteGroupMutation.isPending}
name={groupQuery.data.name}
entity="group"
onConfirm={async () => {
@@ -66,7 +66,7 @@ const GroupSettingsPage: FC = () => {
group={groupQuery.data}
formErrors={groupQuery.error}
isLoading={groupQuery.isLoading}
isUpdating={patchGroupMutation.isLoading}
isUpdating={patchGroupMutation.isPending}
/>
</>
);
+8 -8
View File
@@ -25,14 +25,14 @@ import { GroupsPageView } from "./GroupsPageView";
const GroupsPage: FC = () => {
const { template_rbac: groupsEnabled } = useFeatureVisibility();
const { organization, showOrganizations } = useGroupsSettings();
const groupsQuery = useQuery(
organization ? groupsByOrganization(organization.name) : { enabled: false },
);
const permissionsQuery = useQuery(
organization
? organizationsPermissions([organization.id])
: { enabled: false },
);
const groupsQuery = useQuery({
...groupsByOrganization(organization?.name ?? ""),
enabled: !!organization,
});
const permissionsQuery = useQuery({
...organizationsPermissions([organization?.id ?? ""]),
enabled: !!organization,
});
useEffect(() => {
if (groupsQuery.error) {
@@ -35,7 +35,7 @@ export const DismissWarningButton = (props: { healthcheck: HealthSection }) => {
if (isDismissed) {
return (
<Button
disabled={healthSettingsQuery.isLoading || enableMutation.isLoading}
disabled={healthSettingsQuery.isLoading || enableMutation.isPending}
variant="outline"
onClick={async () => {
const updatedSettings = dismissed_healthchecks.filter(
@@ -48,7 +48,7 @@ export const DismissWarningButton = (props: { healthcheck: HealthSection }) => {
displaySuccess("Warnings enabled successfully!");
}}
>
<Spinner loading={enableMutation.isLoading}>
<Spinner loading={enableMutation.isPending}>
<NotificationsOffOutlined />
</Spinner>
Enable warnings
@@ -58,7 +58,7 @@ export const DismissWarningButton = (props: { healthcheck: HealthSection }) => {
return (
<Button
disabled={healthSettingsQuery.isLoading || dismissMutation.isLoading}
disabled={healthSettingsQuery.isLoading || dismissMutation.isPending}
variant="outline"
onClick={async () => {
const updatedSettings = [...dismissed_healthchecks, props.healthcheck];
@@ -68,7 +68,7 @@ export const DismissWarningButton = (props: { healthcheck: HealthSection }) => {
displaySuccess("Warnings dismissed successfully!");
}}
>
<Spinner loading={dismissMutation.isLoading}>
<Spinner loading={dismissMutation.isPending}>
<NotificationOutlined />
</Spinner>
Dismiss warnings
+1 -1
View File
@@ -31,7 +31,7 @@ export const HealthLayout: FC = () => {
...health(),
refetchInterval: 30_000,
});
const { mutate: forceRefresh, isLoading: isRefreshing } = useMutation(
const { mutate: forceRefresh, isPending: isRefreshing } = useMutation(
refreshHealth(queryClient),
);
const sections = {
@@ -84,8 +84,8 @@ const CreateEditRolePage: FC = () => {
}
isLoading={
role
? updateOrganizationRoleMutation.isLoading
: createOrganizationRoleMutation.isLoading
? updateOrganizationRoleMutation.isPending
: createOrganizationRoleMutation.isPending
}
organizationName={organizationName}
/>
@@ -96,7 +96,7 @@ const CustomRolesPage: FC = () => {
<DeleteDialog
key={roleToDelete?.name}
isOpen={roleToDelete !== undefined}
confirmLoading={deleteRoleMutation.isLoading}
confirmLoading={deleteRoleMutation.isPending}
name={roleToDelete?.name ?? ""}
entity="role"
onCancel={() => setRoleToDelete(undefined)}
@@ -70,11 +70,10 @@ const IdpSyncPage: FC = () => {
const tab = searchParams.get("tab") || "groups";
const field = tab === "groups" ? groupField : roleField;
const fieldValuesQuery = useQuery(
field
? organizationIdpSyncClaimFieldValues(organizationName, field)
: { enabled: false },
);
const fieldValuesQuery = useQuery({
...organizationIdpSyncClaimFieldValues(organizationName, field),
enabled: !!field,
});
if (!organization) {
return <EmptyState message="Organization not found" />;
@@ -98,8 +98,8 @@ const OrganizationMembersPage: FC = () => {
removeMemberMutation.error ??
updateMemberRolesMutation.error
}
isAddingMember={addMemberMutation.isLoading}
isUpdatingMemberRoles={updateMemberRolesMutation.isLoading}
isAddingMember={addMemberMutation.isPending}
isUpdatingMemberRoles={updateMemberRolesMutation.isPending}
me={me}
members={members}
membersQuery={membersQuery}
@@ -51,7 +51,7 @@ interface OrganizationMembersPageViewProps {
me: User;
members: Array<OrganizationMemberTableEntry> | undefined;
membersQuery: PaginationResultInfo & {
isPreviousData: boolean;
isPlaceholderData: boolean;
};
addMember: (user: User) => Promise<void>;
removeMember: (member: OrganizationMemberWithUserData) => void;
@@ -27,12 +27,12 @@ export const CancelJobConfirmationDialog: FC<
const cancelMutation = useMutation({
mutationFn: cancelProvisionerJob,
onSuccess: () => {
queryClient.invalidateQueries(
provisionerJobsQueryKey(job.organization_id),
);
queryClient.invalidateQueries(
getProvisionerDaemonsKey(job.organization_id, job.tags),
);
queryClient.invalidateQueries({
queryKey: provisionerJobsQueryKey(job.organization_id),
});
queryClient.invalidateQueries({
queryKey: getProvisionerDaemonsKey(job.organization_id, job.tags),
});
},
});
@@ -44,7 +44,7 @@ export const CancelJobConfirmationDialog: FC<
description={`Are you sure you want to cancel the provisioner job "${job.id}"? This operation will result in the associated workspaces not getting created.`}
confirmText="Confirm"
cancelText="Discard"
confirmLoading={cancelMutation.isLoading}
confirmLoading={cancelMutation.isPending}
onConfirm={async () => {
try {
await cancelMutation.mutateAsync(job);
@@ -31,7 +31,7 @@ const RequestOTPPage: FC = () => {
) : (
<RequestOTP
error={requestOTPMutation.error}
isRequesting={requestOTPMutation.isLoading}
isRequesting={requestOTPMutation.isPending}
onRequest={(email) => {
requestOTPMutation.mutate({ email });
}}
+1 -1
View File
@@ -56,7 +56,7 @@ export const SetupPage: FC = () => {
</Helmet>
<SetupPageView
authMethods={authMethodsQuery.data}
isLoading={isSigningIn || createFirstUserMutation.isLoading}
isLoading={isSigningIn || createFirstUserMutation.isPending}
error={createFirstUserMutation.error}
onSubmit={async (firstUser) => {
await createFirstUserMutation.mutateAsync(firstUser);
+14 -11
View File
@@ -84,20 +84,23 @@ export const TemplateLayout: FC<PropsWithChildren> = ({
queryKey: ["template", templateName],
queryFn: () => fetchTemplate(organizationName, templateName),
});
const workspacePermissionsQuery = useQuery(
data
? checkAuthorization({
checks: workspacePermissionChecks(
data.template.organization_id,
me.id,
),
})
: { enabled: false },
);
const workspacePermissionsQuery = useQuery({
...checkAuthorization({
checks: workspacePermissionChecks(
data?.template.organization_id ?? "",
me.id,
),
}),
enabled: !!data,
});
const location = useLocation();
const paths = location.pathname.split("/");
const activeTab = paths.at(-1) === templateName ? "summary" : paths.at(-1)!;
const templateNamePath = paths.at(-1);
const activeTab =
templateNamePath === templateName
? "summary"
: (templateNamePath as string);
// Auditors should also be able to view insights, but do not automatically
// have permission to update templates. Need both checks.
const shouldShowInsights =
@@ -19,7 +19,7 @@ const TemplateVersionsPage = () => {
const [latestActiveVersion, setLatestActiveVersion] = useState(
template.active_version_id,
);
const { mutate: promoteVersion, isLoading: isPromoting } = useMutation({
const { mutate: promoteVersion, isPending: isPromoting } = useMutation({
mutationFn: (templateVersionId: string) => {
return API.updateActiveTemplateVersion(template.id, {
id: templateVersionId,
@@ -35,7 +35,7 @@ const TemplateVersionsPage = () => {
},
});
const { mutate: archiveVersion, isLoading: isArchiving } = useMutation({
const { mutate: archiveVersion, isPending: isArchiving } = useMutation({
mutationFn: (templateVersionId: string) => {
return API.archiveTemplateVersion(templateVersionId);
},
@@ -28,35 +28,33 @@ const TemplateSettingsPage: FC = () => {
const {
mutate: updateTemplate,
isLoading: isSubmitting,
isPending: isSubmitting,
error: submitError,
} = useMutation(
(data: UpdateTemplateMeta) => {
} = useMutation({
mutationFn: (data: UpdateTemplateMeta) => {
return API.updateTemplateMeta(template.id, data);
},
{
onSuccess: async (data) => {
// This update has a chance to return a 304 which means nothing was updated.
// In this case, the return payload will be empty and we should use the
// original template data.
if (!data) {
data = template;
} else {
// Only invalid the query if data is returned, indicating at least one field was updated.
//
// we use data.name because an admin may have updated templateName to something new
await queryClient.invalidateQueries(
templateByNameKey(template.organization_name, data.name),
);
}
displaySuccess("Template updated successfully");
navigate(getLink(linkToTemplate(data.organization_name, data.name)));
},
onError: (error) => {
displayError(getErrorMessage(error, "Failed to update template"));
},
onSuccess: async (data) => {
// This update has a chance to return a 304 which means nothing was updated.
// In this case, the return payload will be empty and we should use the
// original template data.
if (!data) {
data = template;
} else {
// Only invalid the query if data is returned, indicating at least one field was updated.
//
// we use data.name because an admin may have updated templateName to something new
await queryClient.invalidateQueries({
queryKey: templateByNameKey(template.organization_name, data.name),
});
}
displaySuccess("Template updated successfully");
navigate(getLink(linkToTemplate(data.organization_name, data.name)));
},
);
onError: (error) => {
displayError(getErrorMessage(error, "Failed to update template"));
},
});
return (
<>
@@ -48,7 +48,7 @@ const TemplatePermissionsPage: FC = () => {
});
reset();
}}
isAddingUser={addUserMutation.isLoading}
isAddingUser={addUserMutation.isPending}
onUpdateUser={async (user, role) => {
await updateUserMutation.mutateAsync({
templateId: template.id,
@@ -58,7 +58,7 @@ const TemplatePermissionsPage: FC = () => {
displaySuccess("User role updated successfully!");
}}
updatingUserId={
updateUserMutation.isLoading
updateUserMutation.isPending
? updateUserMutation.variables?.userId
: undefined
}
@@ -78,7 +78,7 @@ const TemplatePermissionsPage: FC = () => {
});
reset();
}}
isAddingGroup={addGroupMutation.isLoading}
isAddingGroup={addGroupMutation.isPending}
onUpdateGroup={async (group, role) => {
await updateGroupMutation.mutateAsync({
templateId: template.id,
@@ -88,7 +88,7 @@ const TemplatePermissionsPage: FC = () => {
displaySuccess("Group role updated successfully!");
}}
updatingGroupId={
updateGroupMutation.isLoading
updateGroupMutation.isPending
? updateGroupMutation.variables?.groupId
: undefined
}
@@ -7,7 +7,7 @@ import type { Group, ReducedUser } from "api/typesGenerated";
import { AvatarData } from "components/Avatar/AvatarData";
import { useDebouncedFunction } from "hooks/debounce";
import { type ChangeEvent, type FC, useState } from "react";
import { useQuery } from "react-query";
import { keepPreviousData, useQuery } from "react-query";
import { prepareQuery } from "utils/filters";
import { getGroupSubtitle } from "utils/groups";
@@ -36,7 +36,7 @@ export const UserOrGroupAutocomplete: FC<UserOrGroupAutocompleteProps> = ({
limit: 25,
}),
enabled: autoComplete.open,
keepPreviousData: true,
placeholderData: keepPreviousData,
});
const options = aclAvailableQuery.data
? [
@@ -25,22 +25,21 @@ const TemplateSchedulePage: FC = () => {
const {
mutate: updateTemplate,
isLoading: isSubmitting,
isPending: isSubmitting,
error: submitError,
} = useMutation(
(data: UpdateTemplateMeta) => API.updateTemplateMeta(template.id, data),
{
onSuccess: async () => {
await queryClient.invalidateQueries(
templateByNameKey(organizationName, templateName),
);
displaySuccess("Template updated successfully");
// clear browser storage of workspaces impending deletion
localStorage.removeItem("dismissedWorkspaceList"); // workspaces page
localStorage.removeItem("dismissedWorkspace"); // workspace page
},
} = useMutation({
mutationFn: (data: UpdateTemplateMeta) =>
API.updateTemplateMeta(template.id, data),
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: templateByNameKey(organizationName, templateName),
});
displaySuccess("Template updated successfully");
// clear browser storage of workspaces impending deletion
localStorage.removeItem("dismissedWorkspaceList"); // workspaces page
localStorage.removeItem("dismissedWorkspace"); // workspace page
},
);
});
return (
<>
@@ -8,7 +8,7 @@ const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
cacheTime: 0,
gcTime: 0,
refetchOnWindowFocus: false,
networkMode: "offlineFirst",
},
@@ -46,7 +46,7 @@ export const TemplateSettingsLayout: FC = () => {
enabled: templateQuery.isSuccess,
});
if (templateQuery.isLoading || permissionsQuery.isLoading) {
if (!(templateQuery.data && permissionsQuery.data)) {
return <Loader />;
}
@@ -15,7 +15,12 @@ import { Loader } from "components/Loader/Loader";
import { linkToTemplate, useLinks } from "modules/navigation";
import { type FC, useCallback } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery, useQueryClient } from "react-query";
import {
keepPreviousData,
useMutation,
useQuery,
useQueryClient,
} from "react-query";
import { useNavigate, useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { useTemplateSettings } from "../TemplateSettingsLayout";
@@ -36,25 +41,28 @@ const TemplateVariablesPage: FC = () => {
data: version,
error: versionError,
isLoading: isVersionLoading,
} = useQuery({ ...templateVersion(versionId), keepPreviousData: true });
} = useQuery({
...templateVersion(versionId),
placeholderData: keepPreviousData,
});
const {
data: variables,
error: variablesError,
isLoading: isVariablesLoading,
} = useQuery({
...templateVersionVariables(versionId),
keepPreviousData: true,
placeholderData: keepPreviousData,
});
const {
mutateAsync: sendCreateAndBuildTemplateVersion,
error: buildError,
isLoading: isBuilding,
isPending: isBuilding,
} = useMutation(createAndBuildTemplateVersion(organization));
const {
mutateAsync: sendUpdateActiveTemplateVersion,
error: publishError,
isLoading: isPublishing,
isPending: isPublishing,
} = useMutation(updateActiveTemplateVersion(template, queryClient));
const publishVersion = useCallback(
@@ -18,7 +18,12 @@ import { linkToTemplate, useLinks } from "modules/navigation";
import { useWatchVersionLogs } from "modules/templates/useWatchVersionLogs";
import { type FC, useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery, useQueryClient } from "react-query";
import {
keepPreviousData,
useMutation,
useQuery,
useQueryClient,
} from "react-query";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { type FileTree, existsFile, traverse } from "utils/filetree";
import { pageTitle } from "utils/page";
@@ -50,9 +55,9 @@ const TemplateVersionEditorPage: FC = () => {
);
const activeTemplateVersionQuery = useQuery({
...templateVersionOptions,
keepPreviousData: true,
refetchInterval(data) {
return data?.job.status === "pending" ? 1_000 : false;
placeholderData: keepPreviousData,
refetchInterval({ state }) {
return state.data?.job.status === "pending" ? 1_000 : false;
},
});
const { data: activeTemplateVersion } = activeTemplateVersionQuery;
@@ -79,9 +84,9 @@ const TemplateVersionEditorPage: FC = () => {
const publishVersionMutation = useMutation({
mutationFn: publishVersion,
onSuccess: async () => {
await queryClient.invalidateQueries(
templateByNameKey(organizationName, templateName),
);
await queryClient.invalidateQueries({
queryKey: templateByNameKey(organizationName, templateName),
});
},
});
const [lastSuccessfulPublishedVersion, setLastSuccessfulPublishedVersion] =
@@ -183,7 +188,7 @@ const TemplateVersionEditorPage: FC = () => {
navigateToVersion(publishedVersion);
}}
isAskingPublishParameters={isPublishingDialogOpen}
isPublishing={publishVersionMutation.isLoading}
isPublishing={publishVersionMutation.isPending}
publishingError={publishVersionMutation.error}
publishedVersion={lastSuccessfulPublishedVersion}
onCreateWorkspace={() => {
@@ -199,8 +204,8 @@ const TemplateVersionEditorPage: FC = () => {
);
}}
isBuilding={
createTemplateVersionMutation.isLoading ||
uploadFileMutation.isLoading ||
createTemplateVersionMutation.isPending ||
uploadFileMutation.isPending ||
activeTemplateVersion.job.status === "running" ||
activeTemplateVersion.job.status === "pending"
}
@@ -345,9 +350,9 @@ const publishVersion = async (options: {
publishActions.push(API.patchTemplateVersion(version.id, data));
}
if (isActiveVersion) {
if (version.template_id && isActiveVersion) {
publishActions.push(
API.updateActiveTemplateVersion(version.template_id!, {
API.updateActiveTemplateVersion(version.template_id, {
id: version.id,
}),
);
@@ -29,7 +29,7 @@ const AppearancePage: FC = () => {
return (
<>
<AppearanceForm
isUpdating={updateAppearanceSettingsMutation.isLoading}
isUpdating={updateAppearanceSettingsMutation.isPending}
error={updateAppearanceSettingsMutation.error}
initialValues={{
theme_preference: appearanceSettingsQuery.data.theme_preference,
@@ -58,7 +58,7 @@ const ExternalAuthPage: FC = () => {
do so on the oauth2 provider's side."
label="Name of the application to unlink"
isOpen={appToUnlink !== undefined}
confirmLoading={unlinkAppMutation.isLoading}
confirmLoading={unlinkAppMutation.isPending}
name={appToUnlink ?? ""}
entity="application"
onCancel={() => setAppToUnlink(undefined)}
@@ -35,7 +35,7 @@ const OAuth2ProviderPage: FC = () => {
info={`This will invalidate any tokens created by the OAuth2 application "${appToRevoke.name}".`}
label="Name of the application to revoke"
isOpen
confirmLoading={revokeAppMutation.isLoading}
confirmLoading={revokeAppMutation.isPending}
name={appToRevoke.name}
entity="application"
onCancel={() => setAppIdToRevoke(undefined)}
@@ -43,7 +43,7 @@ const SSHKeysPage: FC = () => {
type="delete"
hideCancel={false}
open={isConfirmingRegeneration}
confirmLoading={regenerateSSHKeyMutation.isLoading}
confirmLoading={regenerateSSHKeyMutation.isPending}
title={Language.regenerateDialogTitle}
description={Language.regenerateDialogMessage}
confirmText={Language.confirmLabel}
@@ -2,6 +2,7 @@ import {
updateUserQuietHoursSchedule,
userQuietHoursSchedule,
} from "api/queries/settings";
import type { UserQuietHoursScheduleResponse } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
@@ -25,7 +26,7 @@ const SchedulePage: FC = () => {
const {
mutate: onSubmit,
error: submitError,
isLoading: mutationLoading,
isPending: mutationLoading,
} = useMutation(updateUserQuietHoursSchedule(me.id, queryClient));
if (isLoading) {
@@ -44,7 +45,7 @@ const SchedulePage: FC = () => {
>
<ScheduleForm
isLoading={mutationLoading}
initialValues={quietHoursSchedule}
initialValues={quietHoursSchedule as UserQuietHoursScheduleResponse}
submitError={submitError}
onSubmit={(values) => {
onSubmit(values, {
@@ -33,7 +33,7 @@ const SecurityPage: FC = () => {
form: {
disabled: userLoginType.login_type !== "password",
error: updatePasswordMutation.error,
isLoading: updatePasswordMutation.isLoading,
isLoading: updatePasswordMutation.isPending,
onSubmit: async (data) => {
await updatePasswordMutation.mutateAsync({
userId: me.id,
@@ -25,7 +25,7 @@ const defaultArgs: ComponentProps<typeof SecurityPageView> = {
authMethods: MockAuthMethodsPasswordOnly,
closeConfirmation: action("closeConfirmation"),
confirm: action("confirm"),
error: undefined,
error: null,
isConfirming: false,
isUpdating: false,
openConfirmation: action("openConfirmation"),
@@ -52,7 +52,8 @@ export const useSingleSignOnSection = () => {
const [loginTypeConfirmation, setLoginTypeConfirmation] =
useState<LoginTypeConfirmation>({ open: false, selectedType: undefined });
const mutation = useMutation(API.convertToOAUTH, {
const mutation = useMutation({
mutationFn: API.convertToOAUTH,
onSuccess: (data) => {
const loginTypeMsg =
data.to_type === "github" ? "Github" : "OpenID Connect";
@@ -93,7 +94,7 @@ export const useSingleSignOnSection = () => {
confirm,
// We still want to show it loading when it is success so the modal does not
// change until the redirect
isUpdating: mutation.isLoading || mutation.isSuccess,
isUpdating: mutation.isPending || mutation.isSuccess,
isConfirming: loginTypeConfirmation.open,
error: mutation.error,
};
@@ -7,7 +7,7 @@ const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
cacheTime: 0,
gcTime: 0,
refetchOnWindowFocus: false,
},
},
@@ -18,7 +18,7 @@ export const ConfirmDeleteDialog: FC<ConfirmDeleteDialogProps> = ({
}) => {
const tokenName = token?.token_name;
const { mutate: deleteToken, isLoading: isDeleting } =
const { mutate: deleteToken, isPending: isDeleting } =
useDeleteToken(queryKey);
const onDeleteSuccess = () => {
@@ -29,7 +29,9 @@ export const useDeleteToken = (queryKey: QueryKey) => {
mutationFn: API.deleteToken,
onSuccess: () => {
// Invalidate and refetch
void queryClient.invalidateQueries(queryKey);
void queryClient.invalidateQueries({
queryKey,
});
},
});
};
+5 -5
View File
@@ -143,7 +143,7 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
);
}
}}
isUpdatingUserRoles={updateRolesMutation.isLoading}
isUpdatingUserRoles={updateRolesMutation.isPending}
isLoading={isLoading}
canEditUsers={canEditUsers}
canViewActivity={entitlements.features.audit_log.enabled}
@@ -161,7 +161,7 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
<DeleteDialog
key={userToDelete?.username}
isOpen={userToDelete !== undefined}
confirmLoading={deleteUserMutation.isLoading}
confirmLoading={deleteUserMutation.isPending}
name={userToDelete?.username ?? ""}
entity="user"
onCancel={() => setUserToDelete(undefined)}
@@ -183,7 +183,7 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
type="delete"
hideCancel={false}
open={userToSuspend !== undefined}
confirmLoading={suspendUserMutation.isLoading}
confirmLoading={suspendUserMutation.isPending}
title="Suspend user"
confirmText="Suspend"
onClose={() => setUserToSuspend(undefined)}
@@ -211,7 +211,7 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
type="success"
hideCancel={false}
open={userToActivate !== undefined}
confirmLoading={activateUserMutation.isLoading}
confirmLoading={activateUserMutation.isPending}
title="Activate user"
confirmText="Activate"
onClose={() => setUserToActivate(undefined)}
@@ -238,7 +238,7 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
<ResetPasswordDialog
key={confirmResetPassword?.user.username}
open={confirmResetPassword !== undefined}
loading={updatePasswordMutation.isLoading}
loading={updatePasswordMutation.isPending}
user={confirmResetPassword?.user}
newPassword={confirmResetPassword?.newPassword}
onClose={() => {
@@ -4,7 +4,7 @@ import dayjs from "dayjs";
import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useQuery } from "react-query";
import { keepPreviousData, useQuery } from "react-query";
import { useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView";
@@ -20,7 +20,7 @@ const WorkspaceBuildPage: FC = () => {
const username = params.username.replace("@", "");
const wsBuildQuery = useQuery({
...workspaceBuildByNumber(username, workspaceName, buildNumber),
keepPreviousData: true,
placeholderData: keepPreviousData,
});
const build = wsBuildQuery.data;
const buildsQuery = useQuery({
@@ -36,11 +36,10 @@ const WorkspacePage: FC = () => {
const workspace = workspaceQuery.data;
// Template
const templateQuery = useQuery(
workspace
? templateQueryOptions(workspace.template_id)
: { enabled: false },
);
const templateQuery = useQuery({
...templateQueryOptions(workspace?.template_id ?? ""),
enabled: !!workspace,
});
const template = templateQuery.data;
// Permissions
@@ -67,9 +66,9 @@ const WorkspacePage: FC = () => {
newWorkspaceData.latest_build.status !== workspace.latest_build.status;
if (hasNewBuild || lastBuildHasChanged) {
await queryClient.invalidateQueries(
workspaceBuildsKey(newWorkspaceData.id),
);
await queryClient.invalidateQueries({
queryKey: workspaceBuildsKey(newWorkspaceData.id),
});
}
},
);
@@ -60,7 +60,7 @@ export const WorkspaceReadyPage: FC<WorkspaceReadyPageProps> = ({
open: boolean;
buildParameters?: TypesGen.WorkspaceBuildParameter[];
}>({ open: false });
const { mutate: mutateRestartWorkspace, isLoading: isRestarting } =
const { mutate: mutateRestartWorkspace, isPending: isRestarting } =
useMutation({
mutationFn: API.restartWorkspace,
});
@@ -133,7 +133,8 @@ export const WorkspaceReadyPage: FC<WorkspaceReadyPageProps> = ({
// timings. To refetch the timings, I found the best way was to compare the
// expected amount of script timings that run on start, with the current
// amount of script timings returned in the response.
refetchInterval: (data) => {
refetchInterval: ({ state }) => {
const { data } = state;
const expectedScriptTimingsCount = workspace.latest_build.resources
.flatMap((r) => r.agents)
.flatMap((a) => a?.scripts ?? [])
@@ -13,41 +13,36 @@ const WorkspaceParametersExperimentRouter: FC = () => {
const workspace = useWorkspaceSettings();
const dynamicParametersEnabled = experiments.includes("dynamic-parameters");
const optOutQuery = useQuery(
dynamicParametersEnabled
? {
queryKey: [
"workspace",
workspace.id,
"template_id",
workspace.template_id,
"optOut",
],
queryFn: () => {
const templateId = workspace.template_id;
const workspaceId = workspace.id;
const localStorageKey = optOutKey(templateId);
const storedOptOutString = localStorage.getItem(localStorageKey);
const optOutQuery = useQuery({
enabled: dynamicParametersEnabled,
queryKey: [
"workspace",
workspace.id,
"template_id",
workspace.template_id,
"optOut",
],
queryFn: () => {
const templateId = workspace.template_id;
const workspaceId = workspace.id;
const localStorageKey = optOutKey(templateId);
const storedOptOutString = localStorage.getItem(localStorageKey);
let optOutResult: boolean;
let optOutResult: boolean;
if (storedOptOutString !== null) {
optOutResult = storedOptOutString === "true";
} else {
optOutResult = Boolean(
workspace.template_use_classic_parameter_flow,
);
}
if (storedOptOutString !== null) {
optOutResult = storedOptOutString === "true";
} else {
optOutResult = Boolean(workspace.template_use_classic_parameter_flow);
}
return {
templateId,
workspaceId,
optedOut: optOutResult,
};
},
}
: { enabled: false },
);
return {
templateId,
workspaceId,
optedOut: optOutResult,
};
},
});
if (dynamicParametersEnabled) {
if (optOutQuery.isLoading) {
@@ -63,7 +63,7 @@ const WorkspaceParametersPage: FC = () => {
canChangeVersions={canChangeVersions}
data={parameters.data}
submitError={updateParameters.error}
isSubmitting={updateParameters.isLoading}
isSubmitting={updateParameters.isPending}
onSubmit={(values) => {
if (!parameters.data) {
return;
@@ -222,7 +222,7 @@ const WorkspaceParametersPageExperimental: FC = () => {
canChangeVersions={canChangeVersions}
parameters={sortedParams}
diagnostics={latestResponse.diagnostics}
isSubmitting={updateParameters.isLoading}
isSubmitting={updateParameters.isPending}
onSubmit={handleSubmit}
onCancel={() =>
navigate(`/@${workspace.owner_name}/${workspace.name}`)
@@ -55,12 +55,12 @@ const WorkspaceSchedulePage: FC = () => {
const submitScheduleMutation = useMutation({
mutationFn: submitSchedule,
onSuccess: async () => {
await queryClient.invalidateQueries(
workspaceByOwnerAndNameKey(
await queryClient.invalidateQueries({
queryKey: workspaceByOwnerAndNameKey(
params.username.replace(/^@/, ""),
params.workspace,
),
);
});
displaySuccess("Workspace schedule updated");
},
onError: () => displayError("Failed to update workspace schedule"),
@@ -102,7 +102,7 @@ const WorkspaceSchedulePage: FC = () => {
...getAutostart(workspace),
...getAutostop(workspace),
}}
isLoading={submitScheduleMutation.isLoading}
isLoading={submitScheduleMutation.isPending}
defaultTTL={dayjs.duration(template.default_ttl_ms, "ms").asHours()}
onCancel={() => {
navigate(`/@${username}/${workspaceName}`);
@@ -53,14 +53,16 @@ export const WorkspaceSettingsLayout: FC = () => {
{isError ? (
<ErrorAlert error={error} />
) : (
<WorkspaceSettings.Provider value={workspace}>
<Sidebar workspace={workspace} username={username} />
<Suspense fallback={<Loader />}>
<main css={{ width: "100%" }}>
<Outlet />
</main>
</Suspense>
</WorkspaceSettings.Provider>
workspace && (
<WorkspaceSettings.Provider value={workspace}>
<Sidebar workspace={workspace} username={username} />
<Suspense fallback={<Loader />}>
<main css={{ width: "100%" }}>
<Outlet />
</main>
</Suspense>
</WorkspaceSettings.Provider>
)
)}
</Stack>
</Margins>
@@ -84,7 +84,7 @@ export const WorkspacesButton: FC<WorkspacesButtonProps> = ({
paddingBottom: "8px",
}}
>
{templatesFetchStatus === "loading" ? (
{templatesFetchStatus === "pending" ? (
<Loader size="sm" />
) : (
<>
@@ -77,8 +77,8 @@ const WorkspacesPage: FC = () => {
});
const { data, error, refetch } = useQuery({
...workspacesQueryOptions,
refetchInterval: (_, query) => {
return query.state.error ? false : 5_000;
refetchInterval: ({ state }) => {
return state.error ? false : 5_000;
},
});
@@ -489,9 +489,9 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
});
const isRetrying =
startWorkspaceMutation.isLoading ||
stopWorkspaceMutation.isLoading ||
deleteWorkspaceMutation.isLoading;
startWorkspaceMutation.isPending ||
stopWorkspaceMutation.isPending ||
deleteWorkspaceMutation.isPending;
const retry = () => {
switch (workspace.latest_build.transition) {
@@ -525,7 +525,7 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
{abilities.actions.includes("start") && (
<PrimaryAction
onClick={() => startWorkspaceMutation.mutate({})}
isLoading={startWorkspaceMutation.isLoading}
isLoading={startWorkspaceMutation.isPending}
label="Start workspace"
>
<PlayIcon />
@@ -548,7 +548,7 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
{abilities.canCancel && (
<PrimaryAction
onClick={cancelBuildMutation.mutate}
isLoading={cancelBuildMutation.isLoading}
isLoading={cancelBuildMutation.isPending}
label="Cancel build"
>
<BanIcon />
@@ -94,10 +94,10 @@ export function useBatchActions(options: UseBatchActionsProps) {
deleteAll: deleteAllMutation.mutateAsync,
updateAll: updateAllMutation.mutateAsync,
isLoading:
favoriteAllMutation.isLoading ||
unfavoriteAllMutation.isLoading ||
startAllMutation.isLoading ||
stopAllMutation.isLoading ||
deleteAllMutation.isLoading,
favoriteAllMutation.isPending ||
unfavoriteAllMutation.isPending ||
startAllMutation.isPending ||
stopAllMutation.isPending ||
deleteAllMutation.isPending,
};
}
+3 -11
View File
@@ -22,22 +22,14 @@ import {
import themes, { DEFAULT_THEME } from "theme";
import { MockUserOwner } from "./entities";
// Creates one query client for each test case, to make sure that tests are
// isolated and can't affect each other
export function createTestQueryClient() {
// Helps create one query client for each test case, to make sure that tests
// are isolated and can't affect each other
return new QueryClient({
logger: {
...console,
// Some tests are designed to throw errors as part of their functionality.
// To avoid unnecessary noise from these expected errors, the code is
// structured to suppress them. If this suppression becomes problematic,
// the code can be refactored to handle query errors on a per-test basis.
error: () => {},
},
defaultOptions: {
queries: {
retry: false,
cacheTime: 0,
gcTime: 0,
refetchOnWindowFocus: false,
networkMode: "offlineFirst",
},