aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorJay <jeremy.davis@sonarsource.com>2024-09-11 09:33:38 +0200
committersonartech <sonartech@sonarsource.com>2024-09-12 20:02:53 +0000
commitbae60bb896fb02d58245889bc896c06b62e96cab (patch)
treead594bd1deaeebfdc6869cdaae42ed71da664c95 /server/sonar-web
parentfbb3e8846f006067481102517dbad5126e96213e (diff)
downloadsonarqube-bae60bb896fb02d58245889bc896c06b62e96cab.tar.gz
sonarqube-bae60bb896fb02d58245889bc896c06b62e96cab.zip
SONAR-22970 Improve branch queries and fix loading conflict
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainer.tsx4
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/ProjectAdminPageExtension.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/ProjectPageExtension.tsx7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx23
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx29
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/App.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/issue/Issue.tsx5
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx2
-rw-r--r--server/sonar-web/src/main/js/queries/branch.tsx186
-rw-r--r--server/sonar-web/src/main/js/queries/project-analyses.ts4
27 files changed, 205 insertions, 145 deletions
diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
index 8bfe5f83eca..5df87de7d47 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
@@ -33,7 +33,7 @@ import { getComponentNavigation } from '../../api/navigation';
import { translateWithParameters } from '../../helpers/l10n';
import { HttpStatus } from '../../helpers/request';
import { getPortfolioUrl, getProjectUrl, getPullRequestUrl } from '../../helpers/urls';
-import { useBranchesQuery } from '../../queries/branch';
+import { useCurrentBranchQuery } from '../../queries/branch';
import { useIsLegacyCCTMode } from '../../queries/settings';
import { ProjectAlmBindingConfigurationErrors } from '../../types/alm-settings';
import { Branch } from '../../types/branch-like';
@@ -69,7 +69,7 @@ function ComponentContainer({ hasFeature }: Readonly<WithAvailableFeaturesProps>
React.useState<ProjectAlmBindingConfigurationErrors>();
const [loading, setLoading] = React.useState(true);
const [isPending, setIsPending] = React.useState(false);
- const { data: { branchLike } = {}, isFetching } = useBranchesQuery(
+ const { data: branchLike, isFetching } = useCurrentBranchQuery(
fixedInPullRequest ? component : undefined,
);
diff --git a/server/sonar-web/src/main/js/app/components/extensions/ProjectAdminPageExtension.tsx b/server/sonar-web/src/main/js/app/components/extensions/ProjectAdminPageExtension.tsx
index 2d97554b7d8..b25d4659cbc 100644
--- a/server/sonar-web/src/main/js/app/components/extensions/ProjectAdminPageExtension.tsx
+++ b/server/sonar-web/src/main/js/app/components/extensions/ProjectAdminPageExtension.tsx
@@ -29,7 +29,7 @@ export default function ProjectAdminPageExtension() {
const { component, onComponentChange } = React.useContext(ComponentContext);
// We keep that for compatibility but ideally should advocate to use tanstack query
- const onBranchesChange = useRefreshBranches();
+ const onBranchesChange = useRefreshBranches(component?.key);
const extension = component?.configuration?.extensions?.find(
(p) => p.key === `${pluginKey}/${extensionKey}`,
diff --git a/server/sonar-web/src/main/js/app/components/extensions/ProjectPageExtension.tsx b/server/sonar-web/src/main/js/app/components/extensions/ProjectPageExtension.tsx
index 025a9a20529..c4d6598aef6 100644
--- a/server/sonar-web/src/main/js/app/components/extensions/ProjectPageExtension.tsx
+++ b/server/sonar-web/src/main/js/app/components/extensions/ProjectPageExtension.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { useParams } from 'react-router-dom';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import NotFound from '../NotFound';
import { ComponentContext } from '../componentContext/ComponentContext';
import Extension from './Extension';
@@ -34,13 +34,12 @@ export interface ProjectPageExtensionProps {
export default function ProjectPageExtension({ params }: ProjectPageExtensionProps) {
const { extensionKey, pluginKey } = useParams();
const { component } = React.useContext(ComponentContext);
- const { data } = useBranchesQuery(component);
+ const { data: branchLike, isLoading } = useCurrentBranchQuery(component);
- if (component === undefined || data === undefined) {
+ if (component === undefined || isLoading) {
return null;
}
- const { branchLike } = data;
const fullKey =
params !== undefined
? `${params.pluginKey}/${params.extensionKey}`
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
index 7a668ce3d26..29c0ecc681d 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
@@ -21,10 +21,11 @@ import { TopBar } from 'design-system';
import * as React from 'react';
import { ComponentQualifier } from '~sonar-aligned/types/component';
import NCDAutoUpdateMessage from '../../../../components/new-code-definition/NCDAutoUpdateMessage';
+import { getBranchLikeDisplayName } from '../../../../helpers/branch-like';
import { translate } from '../../../../helpers/l10n';
-import { withBranchLikes } from '../../../../queries/branch';
+import { isDefined } from '../../../../helpers/types';
+import { useCurrentBranchQuery } from '../../../../queries/branch';
import { ProjectAlmBindingConfigurationErrors } from '../../../../types/alm-settings';
-import { Branch } from '../../../../types/branch-like';
import { Feature } from '../../../../types/features';
import { Component } from '../../../../types/types';
import RecentHistory from '../../RecentHistory';
@@ -36,7 +37,6 @@ import Header from './Header';
import Menu from './Menu';
export interface ComponentNavProps extends WithAvailableFeaturesProps {
- branchLike?: Branch;
component: Component;
isInProgress?: boolean;
isPending?: boolean;
@@ -44,8 +44,9 @@ export interface ComponentNavProps extends WithAvailableFeaturesProps {
}
function ComponentNav(props: Readonly<ComponentNavProps>) {
- const { branchLike, component, hasFeature, isInProgress, isPending, projectBindingErrors } =
- props;
+ const { component, hasFeature, isInProgress, isPending, projectBindingErrors } = props;
+
+ const { data: branchLike } = useCurrentBranchQuery(component);
React.useEffect(() => {
const { breadcrumbs, key, name } = component;
@@ -61,6 +62,11 @@ function ComponentNav(props: Readonly<ComponentNavProps>) {
}
}, [component, component.key]);
+ const branchName =
+ hasFeature(Feature.BranchSupport) || !isDefined(branchLike)
+ ? undefined
+ : getBranchLikeDisplayName(branchLike);
+
return (
<>
<TopBar id="context-navigation" aria-label={translate('qualifier', component.qualifier)}>
@@ -69,10 +75,7 @@ function ComponentNav(props: Readonly<ComponentNavProps>) {
</div>
<Menu component={component} isInProgress={isInProgress} isPending={isPending} />
</TopBar>
- <NCDAutoUpdateMessage
- branchName={hasFeature(Feature.BranchSupport) ? undefined : branchLike?.name}
- component={component}
- />
+ <NCDAutoUpdateMessage branchName={branchName} component={component} />
{projectBindingErrors !== undefined && (
<ComponentNavProjectBindingErrorNotif component={component} />
)}
@@ -80,4 +83,4 @@ function ComponentNav(props: Readonly<ComponentNavProps>) {
);
}
-export default withAvailableFeatures(withBranchLikes(ComponentNav));
+export default withAvailableFeatures(ComponentNav);
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
index dd5d943ff8d..6b06dd7ea5b 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
@@ -29,7 +29,7 @@ import { ComponentQualifier } from '~sonar-aligned/types/component';
import { DEFAULT_ISSUES_QUERY } from '../../../../components/shared/utils';
import { hasMessage, translate, translateWithParameters } from '../../../../helpers/l10n';
import { getPortfolioUrl, getProjectQueryUrl } from '../../../../helpers/urls';
-import { useBranchesQuery } from '../../../../queries/branch';
+import { useBranchesQuery, useCurrentBranchQuery } from '../../../../queries/branch';
import { isApplication, isProject } from '../../../../types/component';
import { Feature } from '../../../../types/features';
import { Component, Dict, Extension } from '../../../../types/types';
@@ -64,7 +64,10 @@ type Query = BranchParameters & { id: string };
export function Menu(props: Readonly<Props>) {
const { component, isInProgress, isPending } = props;
const { extensions = [], canBrowseAllChildProjects, qualifier, configuration = {} } = component;
- const { data: { branchLikes, branchLike } = { branchLikes: [] } } = useBranchesQuery(component);
+
+ const { data: branchLikes = [] } = useBranchesQuery(component);
+ const { data: branchLike } = useCurrentBranchQuery(component);
+
const isApplicationChildInaccessble = isApplication(qualifier) && !canBrowseAllChildProjects;
const location = useLocation();
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx
index e4cd9332aa9..86f2fcf9d8a 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx
@@ -25,7 +25,7 @@ import { ComponentQualifier } from '~sonar-aligned/types/component';
import EscKeydownHandler from '../../../../../components/controls/EscKeydownHandler';
import FocusOutHandler from '../../../../../components/controls/FocusOutHandler';
import OutsideClickHandler from '../../../../../components/controls/OutsideClickHandler';
-import { useBranchesQuery } from '../../../../../queries/branch';
+import { useBranchesQuery, useCurrentBranchQuery } from '../../../../../queries/branch';
import { Feature } from '../../../../../types/features';
import { Component } from '../../../../../types/types';
import withAvailableFeatures, {
@@ -46,8 +46,9 @@ export function BranchLikeNavigation(props: BranchLikeNavigationProps) {
component: { configuration },
} = props;
- const { data: { branchLikes, branchLike: currentBranchLike } = { branchLikes: [] } } =
- useBranchesQuery(component);
+ const { data: branchLikes } = useBranchesQuery(component);
+ const { data: currentBranchLike } = useCurrentBranchQuery(component);
+
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
if (currentBranchLike === undefined) {
diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
index e91bfd4b08d..5c5e4384619 100644
--- a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
@@ -17,15 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Spinner } from '@sonarsource/echoes-react';
import * as React from 'react';
import { withRouter } from '~sonar-aligned/components/hoc/withRouter';
-import { isPortfolioLike } from '~sonar-aligned/helpers/component';
import { ComponentQualifier } from '~sonar-aligned/types/component';
import { Location, Router } from '~sonar-aligned/types/router';
import withComponentContext from '../../../app/components/componentContext/withComponentContext';
import withMetricsContext from '../../../app/components/metrics/withMetricsContext';
+import { isDefined } from '../../../helpers/types';
import { CodeScope, getCodeUrl, getProjectUrl } from '../../../helpers/urls';
-import { WithBranchLikesProps, useBranchesQuery } from '../../../queries/branch';
+import { WithBranchLikesProps, useCurrentBranchQuery } from '../../../queries/branch';
import { useComponentBreadcrumbsQuery, useComponentQuery } from '../../../queries/component';
import { useComponentTreeQuery } from '../../../queries/measures';
import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
@@ -33,7 +34,7 @@ import { Component, ComponentMeasure, Dict, Metric } from '../../../types/types'
import { getCodeMetrics } from '../utils';
import CodeAppRenderer from './CodeAppRenderer';
-interface Props extends WithBranchLikesProps {
+interface Props {
component: Component;
location: Location;
metrics: Dict<Metric>;
@@ -43,11 +44,13 @@ interface Props extends WithBranchLikesProps {
const PAGE_SIZE = 100;
function CodeApp(props: Readonly<Props>) {
- const { component, metrics, router, location, branchLike } = props;
+ const { component, metrics, router, location } = props;
const [highlighted, setHighlighted] = React.useState<ComponentMeasure | undefined>();
const [newCodeSelected, setNewCodeSelected] = React.useState<boolean>(true);
const [searchResults, setSearchResults] = React.useState<ComponentMeasure[] | undefined>();
+ const { data: branchLike } = useCurrentBranchQuery(component);
+
const { data: breadcrumbs, isLoading: isBreadcrumbsLoading } = useComponentBreadcrumbsQuery({
component: location.query.selected ?? component.key,
...getBranchLikeQuery(branchLike),
@@ -161,22 +164,16 @@ function CodeApp(props: Readonly<Props>) {
);
}
-function withBranchLikes<P extends { component?: Component }>(
+function withComponentGuard<P extends { component?: Component }>(
WrappedComponent: React.ComponentType<React.PropsWithChildren<P & WithBranchLikesProps>>,
): React.ComponentType<React.PropsWithChildren<Omit<P, 'branchLike' | 'branchLikes'>>> {
- return function WithBranchLike(p: P) {
- const { data, isFetching } = useBranchesQuery(p.component);
-
+ return function WithBranchLike(props: P) {
return (
- (isPortfolioLike(p.component?.qualifier) || !isFetching) && (
- <WrappedComponent
- branchLikes={data?.branchLikes ?? []}
- branchLike={data?.branchLike}
- {...p}
- />
- )
+ <Spinner isLoading={!isDefined(props.component)}>
+ <WrappedComponent {...props} />
+ </Spinner>
);
};
}
-export default withRouter(withComponentContext(withMetricsContext(withBranchLikes(CodeApp))));
+export default withRouter(withComponentContext(withMetricsContext(withComponentGuard(CodeApp))));
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
index af0e880606f..bd7e35b6aa6 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
@@ -48,8 +48,9 @@ import {
areCCTMeasuresComputed,
areSoftwareQualityRatingsComputed,
} from '../../../helpers/measures';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { useMeasuresComponentQuery } from '../../../queries/measures';
+
import { MeasurePageView } from '../../../types/measures';
import { useBubbleChartMetrics } from '../hooks';
import Sidebar from '../sidebar/Sidebar';
@@ -73,7 +74,7 @@ import MeasuresEmpty from './MeasuresEmpty';
export default function ComponentMeasuresApp() {
const { component } = React.useContext(ComponentContext);
- const { data: { branchLike } = {} } = useBranchesQuery(component);
+ const { data: branchLike } = useCurrentBranchQuery(component);
const { query: rawQuery, pathname } = useLocation();
const query = parseQuery(rawQuery);
const router = useRouter();
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
index e3dd0120797..95165e1448a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
@@ -34,7 +34,7 @@ import { getCCTMeasureValue, isDiffMetric } from '../../../helpers/measures';
import { RequestData } from '../../../helpers/request';
import { isDefined } from '../../../helpers/types';
import { getProjectUrl } from '../../../helpers/urls';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { useComponentTreeQuery, useMeasuresComponentQuery } from '../../../queries/measures';
import { useIsLegacyCCTMode } from '../../../queries/settings';
import { useLocation, useRouter } from '../../../sonar-aligned/components/hoc/withRouter';
@@ -68,7 +68,7 @@ export default function MeasureContent(props: Readonly<Props>) {
const { leakPeriod, requestedMetric, rootComponent, updateQuery } = props;
const metrics = useMetrics();
const { query: rawQuery } = useLocation();
- const { data: { branchLike } = {} } = useBranchesQuery();
+ const { data: branchLike } = useCurrentBranchQuery(rootComponent);
const router = useRouter();
const query = parseQuery(rawQuery);
const { data: isLegacy } = useIsLegacyCCTMode();
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
index 52ad7565a03..6a939589dc3 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
@@ -22,7 +22,7 @@ import * as React from 'react';
import { useMetrics } from '../../../app/components/metrics/withMetricsContext';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
import { getProjectUrl } from '../../../helpers/urls';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { useComponentDataQuery } from '../../../queries/component';
import { useComponentTreeQuery } from '../../../queries/measures';
import A11ySkipTarget from '../../../sonar-aligned/components/a11y/A11ySkipTarget';
@@ -54,7 +54,7 @@ interface Props {
export default function MeasureOverview(props: Readonly<Props>) {
const { leakPeriod, updateQuery, rootComponent, bubblesByDomain } = props;
const metrics = useMetrics();
- const { data: { branchLike } = {} } = useBranchesQuery();
+ const { data: branchLike } = useCurrentBranchQuery(rootComponent);
const router = useRouter();
const { query } = useLocation();
const { selected, metric: domain } = parseQuery(query);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
index d6c55e29188..368ceb42fb4 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
@@ -20,8 +20,9 @@
import { Note } from 'design-system';
import React from 'react';
import Measure from '~sonar-aligned/components/measure/Measure';
+import { useComponent } from '../../../app/components/componentContext/withComponentContext';
import { isDiffMetric } from '../../../helpers/measures';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { MeasureEnhanced } from '../../../types/types';
interface Props {
@@ -30,8 +31,10 @@ interface Props {
}
export default function SubnavigationMeasureValue({ measure, componentKey }: Readonly<Props>) {
+ const { component } = useComponent();
+
const isDiff = isDiffMetric(measure.metric.key);
- const { data: { branchLike } = {} } = useBranchesQuery();
+ const { data: branchLike } = useCurrentBranchQuery(component);
const value = isDiff ? measure.leak : measure.value;
return (
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
index 58ce845ca3e..08c95db1ca8 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
@@ -40,7 +40,7 @@ import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
import { translate } from '../../../helpers/l10n';
import { collapsedDirFromPath, fileFromPath } from '../../../helpers/path';
import { getBranchLikeUrl } from '../../../helpers/urls';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { SourceViewerFile } from '../../../types/types';
import { isLoggedIn } from '../../../types/users';
import { IssueOpenInIdeButton } from '../components/IssueOpenInIdeButton';
@@ -79,7 +79,7 @@ export function IssueSourceViewerHeader(props: Readonly<Props>) {
const { measures, path, project, projectName, q } = sourceViewerFile;
const { component } = React.useContext(ComponentContext);
- const { data: branchData, isLoading: isLoadingBranches } = useBranchesQuery(
+ const { data: branchLike, isLoading: isLoadingBranches } = useCurrentBranchQuery(
component ?? {
key: project,
name: projectName,
@@ -89,8 +89,6 @@ export function IssueSourceViewerHeader(props: Readonly<Props>) {
const { currentUser } = useCurrentUser();
const theme = useTheme();
- const branchLike = branchData?.branchLike;
-
const isProjectRoot = q === ComponentQualifier.Project;
const borderColor = themeColor('codeLineBorder')({ theme });
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx
index e79b7b7126a..4a99bdfa447 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx
@@ -61,6 +61,10 @@ let usersHandler: UsersServiceMock;
let timeMarchineHandler: TimeMachineServiceMock;
let qualityGatesHandler: QualityGatesServiceMock;
+jest.mock('../../../../api/ce', () => ({
+ getAnalysisStatus: jest.fn().mockResolvedValue({ component: { warnings: [] } }),
+}));
+
beforeAll(() => {
branchesHandler = new BranchesServiceMock();
measuresHandler = new MeasuresServiceMock();
diff --git a/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx b/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx
index 212cb7725c1..a5daa75ba3d 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx
@@ -24,7 +24,7 @@ import { useLocation } from 'react-router-dom';
import { isBranch, isMainBranch, isPullRequest } from '~sonar-aligned/helpers/branch-like';
import { hasMessage, translate } from '../../../helpers/l10n';
import { getComponentBackgroundTaskUrl } from '../../../helpers/urls';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { BranchLike } from '../../../types/branch-like';
import { Task } from '../../../types/tasks';
import { Component } from '../../../types/types';
@@ -52,7 +52,7 @@ function isSameBranch(task: Task, branchLike?: BranchLike) {
export function AnalysisErrorMessage(props: Props) {
const { component, currentTask } = props;
- const { data: { branchLike } = {} } = useBranchesQuery(component);
+ const { data: branchLike } = useCurrentBranchQuery(component);
const currentTaskOnSameBranch = isSameBranch(currentTask, branchLike);
const location = useLocation();
diff --git a/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx b/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx
index 3efcac9115c..5a4daac6b9e 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx
@@ -38,7 +38,7 @@ interface Props {
export function AnalysisWarningsModal(props: Props) {
const { component, currentUser, warnings } = props;
- const { mutate, isPending, variables } = useDismissBranchWarningMutation();
+ const { mutate, isPending, variables } = useDismissBranchWarningMutation(component.key);
const handleDismissMessage = (messageKey: string) => {
mutate({ component, key: messageKey });
diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
index ead3f2d259e..89e87134d9a 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
@@ -28,7 +28,7 @@ import withAvailableFeatures, {
import withComponentContext from '../../../app/components/componentContext/withComponentContext';
import Suggestions from '../../../components/embed-docs-modal/Suggestions';
import { translate } from '../../../helpers/l10n';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { Feature } from '../../../types/features';
import { Component } from '../../../types/types';
import BranchOverview from '../branches/BranchOverview';
@@ -42,14 +42,12 @@ interface AppProps extends WithAvailableFeaturesProps {
export function App(props: AppProps) {
const { component } = props;
const branchSupportEnabled = props.hasFeature(Feature.BranchSupport);
- const { data } = useBranchesQuery(component);
+ const { data: branchLike } = useCurrentBranchQuery(component);
- if (isPortfolioLike(component.qualifier) || !data) {
+ if (isPortfolioLike(component.qualifier) || !branchLike) {
return null;
}
- const { branchLike, branchLikes } = data;
-
return (
<>
<Helmet defer={false} title={translate('overview.page')} />
@@ -63,11 +61,7 @@ export function App(props: AppProps) {
<Suggestions suggestionGroup="overview" />
{!component.analysisDate && (
- <EmptyOverview
- branchLike={branchLike}
- branchLikes={branchLikes}
- component={component}
- />
+ <EmptyOverview branchLike={branchLike} component={component} />
)}
{component.analysisDate && (
diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
index a8df895e419..3f52c59c316 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
@@ -31,6 +31,7 @@ import { getBranchLikeDisplayName } from '../../../helpers/branch-like';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getProjectTutorialLocation } from '../../../helpers/urls';
import { hasGlobalPermission } from '../../../helpers/users';
+import { useBranchesQuery } from '../../../queries/branch';
import { useTaskForComponentQuery } from '../../../queries/component';
import { AlmKeys } from '../../../types/alm-settings';
import { BranchLike } from '../../../types/branch-like';
@@ -41,13 +42,14 @@ import { CurrentUser, isLoggedIn } from '../../../types/users';
export interface EmptyOverviewProps {
branchLike?: BranchLike;
- branchLikes: BranchLike[];
component: Component;
currentUser: CurrentUser;
}
export function EmptyOverview(props: Readonly<EmptyOverviewProps>) {
- const { branchLike, branchLikes, component, currentUser } = props;
+ const { branchLike, component, currentUser } = props;
+
+ const { data: branchLikes } = useBranchesQuery(component);
const [currentUserCanScanProject, setCurrentUserCanScanProject] = React.useState(
hasGlobalPermission(currentUser, Permissions.Scan),
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx
index 5aecfd6063b..f6952019b2b 100644
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx
@@ -39,6 +39,10 @@ import { CaycStatus } from '../../../../types/types';
import { NoticeType } from '../../../../types/users';
import PullRequestOverview from '../PullRequestOverview';
+jest.mock('../../../../api/ce', () => ({
+ getAnalysisStatus: jest.fn().mockResolvedValue({ component: { warnings: [] } }),
+}));
+
jest.mock('../../../../api/measures', () => {
return {
...jest.requireActual('../../../../sonar-aligned/types/metrics'),
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
index ee3b1c026d0..21cc693dd84 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
@@ -35,7 +35,7 @@ import { mergeRatingMeasureHistory } from '../../../helpers/activity-graph';
import { SOFTWARE_QUALITY_RATING_METRICS } from '../../../helpers/constants';
import { parseDate } from '../../../helpers/dates';
import useApplicationLeakQuery from '../../../queries/applications';
-import { useBranchesQuery } from '../../../queries/branch';
+import { useCurrentBranchQuery } from '../../../queries/branch';
import { useAllMeasuresHistoryQuery } from '../../../queries/measures';
import { useAllProjectAnalysesQuery } from '../../../queries/project-analyses';
import { useIsLegacyCCTMode } from '../../../queries/settings';
@@ -62,7 +62,7 @@ export function ProjectActivityApp() {
const router = useRouter();
const { component } = useComponent();
const metrics = useMetrics();
- const { data: { branchLike } = {}, isFetching: isFetchingBranch } = useBranchesQuery(component);
+ const { data: branchLike, isFetching: isFetchingBranch } = useCurrentBranchQuery(component);
const enabled =
component?.key !== undefined &&
(isPortfolioLike(component?.qualifier) || (Boolean(branchLike) && !isFetchingBranch));
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx
index d7d49d8fad1..51aaf5af4de 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx
@@ -92,7 +92,7 @@ export default function BranchLikeTabs(props: Props) {
}
};
- const { data: { branchLikes } = { branchLikes: [] } } = useBranchesQuery(component);
+ const { data: branchLikes = [] } = useBranchesQuery(component);
const isBranchMode = currentTab === Tabs.Branch;
const branchLikesToDisplay: BranchLike[] = isBranchMode
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx
index 72771d94b84..a1ad559a864 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx
@@ -49,7 +49,7 @@ export interface HotspotHeaderProps {
export function HotspotHeader(props: HotspotHeaderProps) {
const { branchLike, component, hotspot, standards } = props;
const { message, messageFormattings, rule, key } = hotspot;
- const refreshBranchStatus = useRefreshBranchStatus();
+ const refreshBranchStatus = useRefreshBranchStatus(component.key);
const permalink = getPathUrlAsString(
getComponentSecurityHotspotsUrl(component.key, branchLike, {
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx
index 22801e6f3f5..5044e1aba97 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx
@@ -80,7 +80,7 @@ export default function HotspotViewerTabs(props: Props) {
branchLike,
} = props;
- const refreshBranchStatus = useRefreshBranchStatus();
+ const refreshBranchStatus = useRefreshBranchStatus(component.key);
const isSticky = useStickyDetection('.hotspot-tabs', {
offset: TABS_OFFSET,
});
diff --git a/server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx b/server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx
index 3996d5e2125..68d72ffc5b9 100644
--- a/server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx
+++ b/server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx
@@ -21,7 +21,7 @@ import { Note } from 'design-system';
import * as React from 'react';
import { ComponentContext } from '../../app/components/componentContext/ComponentContext';
import { translate } from '../../helpers/l10n';
-import { useBranchesQuery } from '../../queries/branch';
+import { useCurrentBranchQuery } from '../../queries/branch';
import { AnalysisEvent, ProjectAnalysisEventCategory } from '../../types/project-activity';
import Tooltip from '../controls/Tooltip';
import { DefinitionChangeEventInner, isDefinitionChangeEvent } from './DefinitionChangeEventInner';
@@ -39,7 +39,7 @@ export interface EventInnerProps {
export default function EventInner({ event, readonly }: EventInnerProps) {
const { component } = React.useContext(ComponentContext);
- const { data: { branchLike } = {} } = useBranchesQuery(component);
+ const { data: branchLike } = useCurrentBranchQuery(component);
if (isRichQualityGateEvent(event)) {
return <RichQualityGateEventInner event={event} readonly={readonly} />;
} else if (isDefinitionChangeEvent(event)) {
diff --git a/server/sonar-web/src/main/js/components/issue/Issue.tsx b/server/sonar-web/src/main/js/components/issue/Issue.tsx
index 980f08ef931..5a9d5807fbf 100644
--- a/server/sonar-web/src/main/js/components/issue/Issue.tsx
+++ b/server/sonar-web/src/main/js/components/issue/Issue.tsx
@@ -21,6 +21,7 @@ import { flow } from 'lodash';
import * as React from 'react';
import { useCallback } from 'react';
import { setIssueAssignee } from '../../api/issues';
+import { useComponent } from '../../app/components/componentContext/withComponentContext';
import { isInput, isShortcut } from '../../helpers/keyboardEventHelpers';
import { KeyboardKeys } from '../../helpers/keycodes';
import { getKeyboardShortcutEnabled } from '../../helpers/preferences';
@@ -55,7 +56,9 @@ export default function Issue(props: Props) {
onPopupToggle,
} = props;
- const refreshStatus = useRefreshBranchStatus();
+ const { component } = useComponent();
+
+ const refreshStatus = useRefreshBranchStatus(component?.key);
const onChange = flow([props.onChange, refreshStatus]);
diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
index 8209418f3cd..d45e49f4606 100644
--- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
@@ -98,7 +98,7 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
willRefreshAutomatically,
} = props;
- const { data: { branchLikes } = { branchLikes: [] } } = useBranchesQuery(component);
+ const { data: branchLikes = [] } = useBranchesQuery(component);
const mainBranchName =
(branchLikes.find((b) => isMainBranch(b)) as MainBranch | undefined)?.name ||
diff --git a/server/sonar-web/src/main/js/queries/branch.tsx b/server/sonar-web/src/main/js/queries/branch.tsx
index 5b6fd07edbc..6c25f797988 100644
--- a/server/sonar-web/src/main/js/queries/branch.tsx
+++ b/server/sonar-web/src/main/js/queries/branch.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { debounce, flatten } from 'lodash';
import * as React from 'react';
import { useCallback, useContext } from 'react';
@@ -39,14 +39,14 @@ import {
import { dismissAnalysisWarning, getAnalysisStatus } from '../api/ce';
import { getQualityGateProjectStatus } from '../api/quality-gates';
import { AvailableFeaturesContext } from '../app/components/available-features/AvailableFeaturesContext';
+import { useComponent } from '../app/components/componentContext/withComponentContext';
import { extractStatusConditionsFromProjectStatus } from '../helpers/qualityGates';
+import { isDefined } from '../helpers/types';
import { Branch, BranchLike } from '../types/branch-like';
import { isApplication, isProject } from '../types/component';
import { Feature } from '../types/features';
import { Component } from '../types/types';
-
-// This will prevent refresh when navigating from page to page.
-const BRANCHES_STALE_TIME = 30_000;
+import { StaleTime } from './common';
enum InnerState {
Details = 'details',
@@ -54,7 +54,11 @@ enum InnerState {
Status = 'status',
}
-function useBranchesQueryKey(innerState: InnerState, defaultId?: string) {
+/**
+ * @deprecated This is a legacy way of organizing branch keys
+ * It was introduced as a step to remove branch fetching from ComponentContainer.
+ */
+function useBranchesQueryKey(innerState: InnerState, componentKey?: string) {
// Currently, we do not have the component in a react-state ready
// Once we refactor we will be able to fetch it from query state.
// We will be able to make sure that the component is not a portfolio.
@@ -63,38 +67,72 @@ function useBranchesQueryKey(innerState: InnerState, defaultId?: string) {
const { search } = useLocation();
const searchParams = new URLSearchParams(search);
- if (searchParams.has('pullRequest') && searchParams.has('id')) {
+ if (!isDefined(componentKey)) {
+ return ['branches'];
+ }
+
+ if (searchParams.has('pullRequest')) {
return [
'branches',
- searchParams.get('id') as string,
+ componentKey,
'pull-request',
searchParams.get('pullRequest') as string,
innerState,
] as const;
- } else if (searchParams.has('branch') && searchParams.has('id')) {
+ }
+
+ if (searchParams.has('branch')) {
return [
'branches',
- searchParams.get('id') as string,
+ componentKey,
'branch',
searchParams.get('branch') as string,
innerState,
] as const;
- } else if (searchParams.has('fixedInPullRequest') && searchParams.has('id')) {
+ }
+
+ if (searchParams.has('fixedInPullRequest')) {
return [
'branches',
- searchParams.get('id') as string,
+ componentKey,
'fixedInPullRequest',
searchParams.get('fixedInPullRequest') as string,
innerState,
] as const;
- } else if (searchParams.has('id')) {
- return ['branches', searchParams.get('id') as string, innerState] as const;
- } else if (defaultId !== undefined) {
- return ['branches', defaultId, innerState];
}
- return ['branches'];
+
+ return ['branches', componentKey, innerState] as const;
}
+function branchesQuery(
+ component: LightComponent | undefined,
+ branchSupportFeatureEnabled: boolean,
+) {
+ return queryOptions({
+ // we don't care about branchSupportFeatureEnabled in the key, as it never changes during a user session
+ queryKey: ['branches', 'list', component?.key],
+ queryFn: async ({ queryKey: [, , key] }) => {
+ if (component === undefined || key === undefined || isPortfolioLike(component.qualifier)) {
+ return [] as BranchLike[];
+ }
+
+ // Pull Requests exist only for projects and if [branch-support] is enabled
+ const branchLikesPromise =
+ isProject(component.qualifier) && branchSupportFeatureEnabled
+ ? [getBranches(key), getPullRequests(key)]
+ : [getBranches(key)];
+ const branchLikes = await Promise.all(branchLikesPromise).then(flatten<BranchLike>);
+
+ return branchLikes;
+ },
+ enabled: isDefined(component),
+ });
+}
+
+/**
+ * @deprecated This is a legacy way of organizing branch keys
+ * It was introduce as a step to remove branch fetching from ComponentContainer.
+ */
function useMutateBranchQueryKey() {
const { search } = useLocation();
const searchParams = new URLSearchParams(search);
@@ -119,53 +157,55 @@ function getContext(key: ReturnType<typeof useBranchesQueryKey>, branchLike?: Br
return { componentKey, query: {} };
}
-export function useBranchesQuery(component?: LightComponent, refetchInterval?: number) {
+export function useBranchesQuery(component: LightComponent | undefined) {
const features = useContext(AvailableFeaturesContext);
- const key = useBranchesQueryKey(InnerState.Details, component?.key);
return useQuery({
- queryKey: key,
- queryFn: async ({ queryKey: [, key, prOrBranch, name] }) => {
- if (component === undefined || key === undefined) {
- return { branchLikes: [] };
- }
- if (isPortfolioLike(component.qualifier)) {
- return { branchLikes: [] };
- }
-
- const branchLikesPromise =
- isProject(component.qualifier) && features.includes(Feature.BranchSupport)
- ? [getBranches(key), getPullRequests(key)]
- : [getBranches(key)];
- const branchLikes = await Promise.all(branchLikesPromise).then(flatten<BranchLike>);
+ ...branchesQuery(component, features.includes(Feature.BranchSupport)),
+ initialData: [],
+ staleTime: StaleTime.SHORT,
+ });
+}
- let branchLike = branchLikes.find((b) => isBranch(b) && b.isMain);
+export function useCurrentBranchQuery(component: LightComponent | undefined) {
+ const features = useContext(AvailableFeaturesContext);
+ const { search } = useLocation();
- if (prOrBranch === 'pull-request') {
- branchLike = branchLikes.find((b) => isPullRequest(b) && b.key === name);
- } else if (prOrBranch === 'branch') {
- branchLike = branchLikes.find((b) => isBranch(b) && b.name === name);
- } else if (prOrBranch === 'fixedInPullRequest') {
- const targetBranch = branchLikes.filter(isPullRequest).find((b) => b.key === name)?.target;
- branchLike = branchLikes.find((b) => isBranch(b) && b.name === targetBranch);
+ const select = useCallback(
+ (branchLikes: BranchLike[]) => {
+ const searchParams = new URLSearchParams(search);
+ if (searchParams.has('branch')) {
+ return branchLikes.find((b) => isBranch(b) && b.name === searchParams.get('branch'));
+ } else if (searchParams.has('pullRequest')) {
+ return branchLikes.find(
+ (b) => isPullRequest(b) && b.key === searchParams.get('pullRequest'),
+ );
+ } else if (searchParams.has('fixedInPullRequest')) {
+ const targetBranch = branchLikes
+ .filter(isPullRequest)
+ .find((b) => b.key === searchParams.get('fixedInPullRequest'))?.target;
+ return branchLikes.find((b) => isBranch(b) && b.name === targetBranch);
}
- return { branchLikes, branchLike };
+ return branchLikes.find((b) => isBranch(b) && b.isMain);
},
- // The check of the key must disappear once component state is in react-query
- enabled: !!component && component.key === key[1],
- staleTime: refetchInterval ?? BRANCHES_STALE_TIME,
- refetchInterval,
+ [search],
+ );
+
+ return useQuery({
+ ...branchesQuery(component, features.includes(Feature.BranchSupport)),
+ select,
+ staleTime: StaleTime.LIVE,
});
}
export function useBranchStatusQuery(component: Component) {
- const branchQuery = useBranchesQuery(component);
- const key = useBranchesQueryKey(InnerState.Status);
+ const { data: branchLike } = useCurrentBranchQuery(component);
+ const key = useBranchesQueryKey(InnerState.Status, component.key);
return useQuery({
queryKey: key,
queryFn: async ({ queryKey }) => {
- const { query } = getContext(queryKey, branchQuery.data?.branchLike);
+ const { query } = getContext(queryKey, branchLike);
if (!isProject(component.qualifier)) {
return {};
}
@@ -186,14 +226,13 @@ export function useBranchStatusQuery(component: Component) {
};
},
enabled: isProject(component.qualifier) || isApplication(component.qualifier),
- staleTime: BRANCHES_STALE_TIME,
+ staleTime: StaleTime.SHORT,
});
}
export function useBranchWarningQuery(component: Component) {
- const branchQuery = useBranchesQuery(component);
- const branchLike = branchQuery.data?.branchLike;
- const key = useBranchesQueryKey(InnerState.Warning);
+ const { data: branchLike } = useCurrentBranchQuery(component);
+ const key = useBranchesQueryKey(InnerState.Warning, component.key);
return useQuery({
queryKey: key,
queryFn: async ({ queryKey }) => {
@@ -205,20 +244,21 @@ export function useBranchWarningQuery(component: Component) {
return branchStatus.warnings;
},
enabled: !!branchLike && isProject(component.qualifier) && component.key === key[1],
- staleTime: BRANCHES_STALE_TIME,
+ staleTime: StaleTime.SHORT,
});
}
-export function useDismissBranchWarningMutation() {
+export function useDismissBranchWarningMutation(componentKey: string | undefined) {
type DismissArg = { component: Component; key: string };
const queryClient = useQueryClient();
- const invalidateKey = useBranchesQueryKey(InnerState.Warning);
+ const invalidateKey = useBranchesQueryKey(InnerState.Warning, componentKey);
return useMutation({
mutationFn: async ({ component, key }: DismissArg) => {
await dismissAnalysisWarning(component.key, key);
},
- onSuccess() {
+ onSuccess(_1, { component }) {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', component.key] });
queryClient.invalidateQueries({ queryKey: invalidateKey });
},
});
@@ -234,7 +274,8 @@ export function useExcludeFromPurgeMutation() {
mutationFn: async ({ component, key, exclude }: ExcludeFromPurgeArg) => {
await excludeBranchFromPurge(component.key, key, exclude);
},
- onSuccess() {
+ onSuccess(_1, { component }) {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', component.key] });
queryClient.invalidateQueries({ queryKey: invalidateKey });
},
});
@@ -277,8 +318,9 @@ export function useDeletBranchMutation() {
}
return { navigate: false };
},
- onSuccess({ navigate }) {
+ onSuccess({ navigate }, { component }) {
if (!navigate) {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', component.key] });
queryClient.invalidateQueries({ queryKey: invalidateKey });
}
},
@@ -294,7 +336,8 @@ export function useRenameMainBranchMutation() {
mutationFn: async ({ component, name }: RenameMainBranchArg) => {
await renameBranch(component.key, name);
},
- onSuccess() {
+ onSuccess(_1, { component }) {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', component.key] });
queryClient.invalidateQueries({ queryKey: invalidateKey });
},
});
@@ -309,7 +352,8 @@ export function useSetMainBranchMutation() {
mutationFn: async ({ component, branchName }: SetAsMainBranchArg) => {
await setMainBranch(component.key, branchName);
},
- onSuccess() {
+ onSuccess(_1, { component }) {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', component.key] });
queryClient.invalidateQueries({ queryKey: invalidateKey });
},
});
@@ -321,13 +365,14 @@ export function useSetMainBranchMutation() {
*/
const REFRESH_INTERVAL = 1_000;
-export function useRefreshBranchStatus(): () => void {
+export function useRefreshBranchStatus(componentKey: string | undefined): () => void {
const queryClient = useQueryClient();
- const invalidateStatusKey = useBranchesQueryKey(InnerState.Status);
- const invalidateDetailsKey = useBranchesQueryKey(InnerState.Details);
+ const invalidateStatusKey = useBranchesQueryKey(InnerState.Status, componentKey);
+ const invalidateDetailsKey = useBranchesQueryKey(InnerState.Details, componentKey);
return useCallback(
debounce(() => {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', componentKey] });
queryClient.invalidateQueries({
queryKey: invalidateStatusKey,
});
@@ -339,11 +384,12 @@ export function useRefreshBranchStatus(): () => void {
);
}
-export function useRefreshBranches() {
+export function useRefreshBranches(componentKey: string | undefined) {
const queryClient = useQueryClient();
const invalidateKey = useMutateBranchQueryKey();
return () => {
+ queryClient.invalidateQueries({ queryKey: ['branches', 'list', componentKey] });
queryClient.invalidateQueries({ queryKey: invalidateKey });
};
}
@@ -358,12 +404,13 @@ export function withBranchLikes<P extends { component?: Component }>(
WrappedComponent: React.ComponentType<React.PropsWithChildren<P & WithBranchLikesProps>>,
): React.ComponentType<React.PropsWithChildren<Omit<P, 'branchLike' | 'branchLikes'>>> {
return function WithBranchLike(p: P) {
- const { data, isFetching } = useBranchesQuery(p.component);
+ const { data: branchLikes, isLoading } = useBranchesQuery(p.component);
+ const { data: branchLike, isFetching } = useCurrentBranchQuery(p.component);
return (
<WrappedComponent
- branchLikes={data?.branchLikes ?? []}
- branchLike={data?.branchLike}
- isFetchingBranch={!isPortfolioLike(p.component?.qualifier) && isFetching}
+ branchLikes={branchLikes ?? []}
+ branchLike={branchLike}
+ isFetchingBranch={!isPortfolioLike(p.component?.qualifier) && (isFetching || isLoading)}
{...p}
/>
);
@@ -376,7 +423,8 @@ export function withBranchStatusRefresh<
WrappedComponent: React.ComponentType<React.PropsWithChildren<P>>,
): React.ComponentType<React.PropsWithChildren<Omit<P, 'refreshBranchStatus'>>> {
return function WithBranchStatusRefresh(props: P) {
- const refresh = useRefreshBranchStatus();
+ const { component } = useComponent();
+ const refresh = useRefreshBranchStatus(component?.key);
return <WrappedComponent {...props} refreshBranchStatus={refresh} />;
};
diff --git a/server/sonar-web/src/main/js/queries/project-analyses.ts b/server/sonar-web/src/main/js/queries/project-analyses.ts
index 02186c97420..ee6fe2a0e73 100644
--- a/server/sonar-web/src/main/js/queries/project-analyses.ts
+++ b/server/sonar-web/src/main/js/queries/project-analyses.ts
@@ -37,14 +37,14 @@ import {
import { parseDate } from '../helpers/dates';
import { serializeStringArray } from '../helpers/query';
import { ParsedAnalysis } from '../types/project-activity';
-import { useBranchesQuery } from './branch';
+import { useCurrentBranchQuery } from './branch';
const ACTIVITY_PAGE_SIZE = 500;
function useProjectActivityQueryKey() {
const { component } = useComponent();
const componentKey = useTopLevelComponentKey();
- const { data: { branchLike } = {} } = useBranchesQuery(component);
+ const { data: branchLike } = useCurrentBranchQuery(component);
const branchParams = getBranchLikeQuery(branchLike);
return ['activity', 'list', componentKey, branchParams] as [