]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15913 Extract users from redux
authorPhilippe Perrin <philippe.perrin@sonarsource.com>
Tue, 8 Mar 2022 15:59:11 +0000 (16:59 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 14 Mar 2022 20:03:08 +0000 (20:03 +0000)
177 files changed:
server/sonar-web/src/main/js/api/quality-gates.ts
server/sonar-web/src/main/js/api/security-hotspots.ts
server/sonar-web/src/main/js/api/users.ts
server/sonar-web/src/main/js/app/components/Landing.tsx
server/sonar-web/src/main/js/app/components/PluginRiskConsent.tsx
server/sonar-web/src/main/js/app/components/ResetPassword.tsx
server/sonar-web/src/main/js/app/components/StartupModal.tsx
server/sonar-web/src/main/js/app/components/__tests__/Landing-test.tsx
server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap
server/sonar-web/src/main/js/app/components/current-user/CurrentUserContext.ts [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/current-user/CurrentUserContextProvider.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/current-user/withCurrentUserContext.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/extensions/Extension.tsx
server/sonar-web/src/main/js/app/components/extensions/__tests__/__snapshots__/ProjectAdminPageExtension-test.tsx.snap
server/sonar-web/src/main/js/app/components/extensions/__tests__/__snapshots__/ProjectPageExtension-test.tsx.snap
server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
server/sonar-web/src/main/js/app/components/nav/component/Header.tsx
server/sonar-web/src/main/js/app/components/nav/component/HeaderMeta.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/HeaderMeta-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.tsx
server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
server/sonar-web/src/main/js/app/components/promotion-notification/__tests__/PromotionNotification-test.tsx
server/sonar-web/src/main/js/app/components/search/Search.tsx
server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
server/sonar-web/src/main/js/app/components/update-notification/__tests__/UpdateNotification-test.tsx
server/sonar-web/src/main/js/app/components/update-notification/__tests__/__snapshots__/UpdateNotification-test.tsx.snap
server/sonar-web/src/main/js/app/utils/getStore.ts
server/sonar-web/src/main/js/app/utils/startReactApp.tsx
server/sonar-web/src/main/js/apps/account/components/Account.tsx
server/sonar-web/src/main/js/apps/account/components/Security.tsx
server/sonar-web/src/main/js/apps/account/components/UserCard.tsx
server/sonar-web/src/main/js/apps/account/components/__tests__/Security-test.tsx
server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.tsx
server/sonar-web/src/main/js/apps/account/notifications/SonarCloudNotifications.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/notifications/__tests__/SonarCloudNotifications-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.tsx.snap
server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/SonarCloudNotifications-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/account/profile/Profile.tsx
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx
server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx
server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/AppContainer-test.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/PageActions-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
server/sonar-web/src/main/js/apps/issues/utils.ts
server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverviewRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanel-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
server/sonar-web/src/main/js/apps/overview/components/SonarLintPromotion.tsx
server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap
server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts [deleted file]
server/sonar-web/src/main/js/apps/permissions/routes.ts
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx
server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx
server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.tsx
server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCreationMenu.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/DefaultPageSelector-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx
server/sonar-web/src/main/js/apps/projectsManagement/App.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx
server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/PermissionItem.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissions.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsAddModal.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsAddModalRenderer.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx
server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/FilterBar.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotPrimaryLocationBox.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistoryAndComments.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetHeader.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistoryAndComments-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/Assignee.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/AssigneeRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/AssigneeSelection.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/AssigneeSelectionRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/Assignee-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/AssigneeRenderer-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/AssigneeSelection-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/AssigneeSelectionRenderer-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/status/Status.tsx
server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx
server/sonar-web/src/main/js/apps/users/UsersApp.tsx
server/sonar-web/src/main/js/apps/users/UsersList.tsx
server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx
server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx
server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx
server/sonar-web/src/main/js/apps/users/components/UserActions.tsx
server/sonar-web/src/main/js/apps/users/components/UserForm.tsx
server/sonar-web/src/main/js/apps/users/components/UserGroups.tsx
server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx
server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx
server/sonar-web/src/main/js/apps/users/components/__tests__/UserListItem-test.tsx
server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx
server/sonar-web/src/main/js/components/common/ResetPasswordForm.tsx
server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx
server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
server/sonar-web/src/main/js/components/controls/__tests__/HomePageSelect-test.tsx
server/sonar-web/src/main/js/components/hoc/__tests__/whenLoggedIn-test.tsx
server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx [deleted file]
server/sonar-web/src/main/js/components/hoc/whenLoggedIn.tsx
server/sonar-web/src/main/js/components/hoc/withCurrentUser.tsx [deleted file]
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.tsx
server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx
server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/BitbucketPipelinesTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/RepositoryVariables.tsx
server/sonar-web/src/main/js/components/tutorials/components/EditTokenModal.tsx
server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx
server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx
server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/PreRequisitesStep-test.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/manual/TokenStep.tsx
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/TokenStep-test.tsx
server/sonar-web/src/main/js/helpers/__tests__/users-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/issues.ts
server/sonar-web/src/main/js/helpers/mocks/users.ts
server/sonar-web/src/main/js/helpers/testMocks.ts
server/sonar-web/src/main/js/helpers/urls.ts
server/sonar-web/src/main/js/helpers/users.ts
server/sonar-web/src/main/js/store/__tests__/users-test.tsx [deleted file]
server/sonar-web/src/main/js/store/rootReducer.ts
server/sonar-web/src/main/js/store/users.ts [deleted file]
server/sonar-web/src/main/js/types/extension.ts
server/sonar-web/src/main/js/types/issues.ts
server/sonar-web/src/main/js/types/quality-gates.ts
server/sonar-web/src/main/js/types/security-hotspots.ts
server/sonar-web/src/main/js/types/types.ts
server/sonar-web/src/main/js/types/users.ts [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index cc3d83be8e0d37de6e142c2d4af79b8047b61f13..e28492899cd934cd665b4eae6c001c0a09feac70 100644 (file)
@@ -28,7 +28,8 @@ import {
   QualityGateProjectStatus,
   SearchPermissionsParameters
 } from '../types/quality-gates';
-import { Condition, Paging, QualityGate, UserBase } from '../types/types';
+import { Condition, Paging, QualityGate } from '../types/types';
+import { UserBase } from '../types/users';
 
 export function fetchQualityGates(): Promise<{
   actions: { create: boolean };
index e1f45cb1a19a176bc698e42f97cf0ff50322ce52..2f2608b2b3b9503a5a9ef7aa094ac5e63b908083 100644 (file)
@@ -29,7 +29,7 @@ import {
   HotspotSetStatusRequest,
   HotspotStatus
 } from '../types/security-hotspots';
-import { UserBase } from '../types/types';
+import { UserBase } from '../types/users';
 
 const HOTSPOTS_SEARCH_URL = '/api/hotspots/search';
 
index 6da4a35d8e0e931833168b6f09c4c5a548d0fd5b..272c2c73295935ad358b72c26fd15c26d968afa2 100644 (file)
  */
 import throwGlobalError from '../app/utils/throwGlobalError';
 import { getJSON, post, postJSON } from '../helpers/request';
-import {
-  CurrentUser,
-  CurrentUserSetting,
-  HomePage,
-  IdentityProvider,
-  Paging,
-  User
-} from '../types/types';
+import { IdentityProvider, Paging } from '../types/types';
+import { CurrentUser, HomePage, User } from '../types/users';
 
 export function getCurrentUser(): Promise<CurrentUser> {
   return getJSON('/api/users/current');
@@ -102,10 +96,6 @@ export function setHomePage(homepage: HomePage): Promise<void | Response> {
   return post('/api/users/set_homepage', homepage).catch(throwGlobalError);
 }
 
-export function setUserSetting(setting: CurrentUserSetting): Promise<void | Response> {
-  return post('/api/users/set_setting', setting).catch(throwGlobalError);
-}
-
 export function dismissSonarlintAd(): Promise<void | Response> {
   return post('/api/users/dismiss_sonarlint_ad').catch(throwGlobalError);
 }
index ede739b43fe7de9428a68b840bf2684b1071adc1..131091752905b642130083c5f5f2424723d2fd8d 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { withCurrentUser } from '../../components/hoc/withCurrentUser';
 import { Router, withRouter } from '../../components/hoc/withRouter';
 import { getHomePageUrl } from '../../helpers/urls';
-import { isLoggedIn } from '../../helpers/users';
-import { CurrentUser } from '../../types/types';
+import { CurrentUser, isLoggedIn } from '../../types/users';
+import withCurrentUserContext from './current-user/withCurrentUserContext';
 
 export interface LandingProps {
   currentUser: CurrentUser;
@@ -45,4 +44,4 @@ export class Landing extends React.PureComponent<LandingProps> {
   }
 }
 
-export default withRouter(withCurrentUser(Landing));
+export default withRouter(withCurrentUserContext(Landing));
index ab85767d669a03466aa5a4d164f1ee005abacc49..6b560a90ca21012334b8fee2c005ea0ec9d9275c 100644 (file)
@@ -27,7 +27,7 @@ import { hasGlobalPermission } from '../../helpers/users';
 import { Permissions } from '../../types/permissions';
 import { RiskConsent } from '../../types/plugins';
 import { SettingsKey } from '../../types/settings';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import GlobalMessagesContainer from './GlobalMessagesContainer';
 import './PluginRiskConsent.css';
 
index 800b9e05c3bc84f22e6fdc678934c34f9b5b3702..183cd2accb8274398467ac8c4f7771ab99aa39f4 100644 (file)
@@ -22,7 +22,7 @@ import ResetPasswordForm from '../../components/common/ResetPasswordForm';
 import { whenLoggedIn } from '../../components/hoc/whenLoggedIn';
 import { translate } from '../../helpers/l10n';
 import { getBaseUrl } from '../../helpers/system';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import GlobalMessagesContainer from './GlobalMessagesContainer';
 
 export interface ResetPasswordProps {
index 5b7c3915672ecabe0046e1884d30bde55de9b0d4..ebbdebed5871a65b421995459b63462eec2d5460 100644 (file)
  */
 import { differenceInDays } from 'date-fns';
 import * as React from 'react';
-import { connect } from 'react-redux';
 import { showLicense } from '../../api/marketplace';
 import { Location, Router, withRouter } from '../../components/hoc/withRouter';
 import { lazyLoadComponent } from '../../components/lazyLoadComponent';
 import { parseDate, toShortNotSoISOString } from '../../helpers/dates';
 import { hasMessage } from '../../helpers/l10n';
 import { get, save } from '../../helpers/storage';
-import { isLoggedIn } from '../../helpers/users';
-import { getCurrentUser, Store } from '../../store/rootReducer';
 import { AppState } from '../../types/appstate';
 import { EditionKey } from '../../types/editions';
-import { CurrentUser } from '../../types/types';
+import { CurrentUser, isLoggedIn } from '../../types/users';
 import withAppStateContext from './app-state/withAppStateContext';
+import withCurrentUserContext from './current-user/withCurrentUserContext';
 
 const LicensePromptModal = lazyLoadComponent(
   () => import('../../apps/marketplace/components/LicensePromptModal'),
@@ -98,8 +96,4 @@ export class StartupModal extends React.PureComponent<Props & StateProps, State>
   }
 }
 
-const mapStateToProps = (state: Store): StateProps => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(withRouter(withAppStateContext(StartupModal)));
+export default withCurrentUserContext(withRouter(withAppStateContext(StartupModal)));
index 102f5f819fa89d8a088572262247b2569dc15246..437ef87cf294e8a7fa00152378349cbe32de4f8c 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { mockCurrentUser, mockLoggedInUser, mockRouter } from '../../../helpers/testMocks';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser } from '../../../types/users';
 import { Landing } from '../Landing';
 
 it.each([
index 4a07e47012b54b6a3bea378577f9c5b58b164777..0813389c9ba0d4ce621002f306c7a889faafb8a4 100644 (file)
@@ -27,7 +27,7 @@ import { get, save } from '../../../helpers/storage';
 import { mockAppState } from '../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../helpers/testUtils';
 import { EditionKey } from '../../../types/editions';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import { StartupModal } from '../StartupModal';
 
 jest.mock('../../../api/marketplace', () => ({
index cf997f458f28b2bcb78f9e761730799a6c4b9d91..ab5e59052290f6aaa1109f3e00961e0564faae7b 100644 (file)
@@ -3,7 +3,7 @@
 exports[`should render correctly 1`] = `
 <SuggestionsProvider>
   <A11yProvider>
-    <Connect(withRouter(withAppStateContext(StartupModal)))>
+    <withCurrentUserContext(withRouter(withAppStateContext(StartupModal)))>
       <A11ySkipLinks />
       <div
         className="global-container"
@@ -19,7 +19,7 @@ exports[`should render correctly 1`] = `
               <withAppStateContext(IndexationContextProvider)>
                 <LanguagesContextProvider>
                   <MetricsContextProvider>
-                    <Connect(GlobalNav)
+                    <withCurrentUserContext(GlobalNav)
                       location={
                         Object {
                           "action": "PUSH",
@@ -33,8 +33,8 @@ exports[`should render correctly 1`] = `
                       }
                     />
                     <Connect(GlobalMessages) />
-                    <Connect(withCurrentUser(withIndexationContext(IndexationNotification))) />
-                    <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
+                    <withCurrentUserContext(withIndexationContext(IndexationNotification)) />
+                    <withCurrentUserContext(withAppStateContext(UpdateNotification))
                       dismissable={true}
                     />
                     <ChildComponent />
@@ -43,11 +43,11 @@ exports[`should render correctly 1`] = `
               </withAppStateContext(IndexationContextProvider)>
             </Workspace>
           </div>
-          <Connect(Connect(withCurrentUser(PromotionNotification))) />
+          <withCurrentUserContext(PromotionNotification) />
         </div>
         <withAppStateContext(GlobalFooter) />
       </div>
-    </Connect(withRouter(withAppStateContext(StartupModal)))>
+    </withCurrentUserContext(withRouter(withAppStateContext(StartupModal)))>
   </A11yProvider>
 </SuggestionsProvider>
 `;
diff --git a/server/sonar-web/src/main/js/app/components/current-user/CurrentUserContext.ts b/server/sonar-web/src/main/js/app/components/current-user/CurrentUserContext.ts
new file mode 100644 (file)
index 0000000..efb1053
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { CurrentUser, HomePage } from '../../../types/users';
+
+export interface CurrentUserContextInterface {
+  currentUser: CurrentUser;
+  updateCurrentUserHomepage: (homepage: HomePage) => void;
+  updateCurrentUserSonarLintAdSeen: () => void;
+}
+
+export const CurrentUserContext = React.createContext<CurrentUserContextInterface | undefined>(
+  undefined
+);
diff --git a/server/sonar-web/src/main/js/app/components/current-user/CurrentUserContextProvider.tsx b/server/sonar-web/src/main/js/app/components/current-user/CurrentUserContextProvider.tsx
new file mode 100644 (file)
index 0000000..9ffe3d1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { CurrentUser, HomePage } from '../../../types/users';
+import { CurrentUserContext } from './CurrentUserContext';
+
+interface Props {
+  currentUser?: CurrentUser;
+}
+
+interface State {
+  currentUser: CurrentUser;
+}
+
+export default class CurrentUserContextProvider extends React.PureComponent<Props, State> {
+  constructor(props: Props) {
+    super(props);
+    this.state = { currentUser: props.currentUser ?? { isLoggedIn: false } };
+  }
+
+  updateCurrentUserHomepage = (homepage: HomePage) => {
+    this.setState(prevState => ({
+      currentUser: { ...prevState.currentUser, homepage }
+    }));
+  };
+
+  updateCurrentUserSonarLintAdSeen = () => {
+    this.setState(prevState => ({
+      currentUser: { ...prevState.currentUser, sonarLintAdSeen: true }
+    }));
+  };
+
+  render() {
+    return (
+      <CurrentUserContext.Provider
+        value={{
+          currentUser: this.state.currentUser,
+          updateCurrentUserHomepage: this.updateCurrentUserHomepage,
+          updateCurrentUserSonarLintAdSeen: this.updateCurrentUserSonarLintAdSeen
+        }}>
+        {this.props.children}
+      </CurrentUserContext.Provider>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/app/components/current-user/withCurrentUserContext.tsx b/server/sonar-web/src/main/js/app/components/current-user/withCurrentUserContext.tsx
new file mode 100644 (file)
index 0000000..e2621f0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { getWrappedDisplayName } from '../../../components/hoc/utils';
+import { CurrentUserContext, CurrentUserContextInterface } from './CurrentUserContext';
+
+export default function withCurrentUserContext<P>(
+  WrappedComponent: React.ComponentType<P & Pick<CurrentUserContextInterface, 'currentUser'>>
+) {
+  return class WithCurrentUserContext extends React.PureComponent<
+    Omit<P, keyof CurrentUserContextInterface>
+  > {
+    static displayName = getWrappedDisplayName(WrappedComponent, 'withCurrentUserContext');
+
+    render() {
+      return (
+        <CurrentUserContext.Consumer>
+          {(currentUserContext: CurrentUserContextInterface) => (
+            <WrappedComponent {...currentUserContext} {...(this.props as P)} />
+          )}
+        </CurrentUserContext.Consumer>
+      );
+    }
+  };
+}
index db90d3e034223869421ad85b88800359e869882f..695cd5f3efcfb92a00bb2cc0f70ed26d178d831e 100644 (file)
@@ -26,13 +26,14 @@ import { getExtensionStart } from '../../../helpers/extensions';
 import { getCurrentL10nBundle, translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/system';
 import { addGlobalErrorMessage } from '../../../store/globalMessages';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
 import { AppState } from '../../../types/appstate';
 import { ExtensionStartMethod } from '../../../types/extension';
-import { CurrentUser, Dict, Extension as TypeExtension } from '../../../types/types';
+import { Dict, Extension as TypeExtension } from '../../../types/types';
+import { CurrentUser } from '../../../types/users';
 import * as theme from '../../theme';
 import getStore from '../../utils/getStore';
 import withAppStateContext from '../app-state/withAppStateContext';
+import withCurrentUserContext from '../current-user/withCurrentUserContext';
 
 interface Props extends WrappedComponentProps {
   appState: AppState;
@@ -126,9 +127,10 @@ export class Extension extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: Store) => ({ currentUser: getCurrentUser(state) });
-const mapDispatchToProps = { onFail: addGlobalErrorMessage };
-
 export default injectIntl(
-  withRouter(withAppStateContext(connect(mapStateToProps, mapDispatchToProps)(Extension)))
+  withRouter(
+    withAppStateContext(
+      withCurrentUserContext(connect(null, { onFail: addGlobalErrorMessage })(Extension))
+    )
+  )
 );
index bb720539c93c406c4a3d5bdde6bca4a915e17fb3..41c11c85e36ba672c18a5f10e283ff96ddaa7ace 100644 (file)
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly: extension exists 1`] = `
-<injectIntl(withRouter(withAppStateContext(Connect(Extension))))
+<injectIntl(withRouter(withAppStateContext(withCurrentUserContext(Connect(Extension)))))
   extension={
     Object {
       "key": "foo/bar",
index 53b7d0d8243bef0dc38d542220ada3baf6f14925..bb710428d4c5dcb79114d986142389901ef9df14 100644 (file)
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-<injectIntl(withRouter(withAppStateContext(Connect(Extension))))
+<injectIntl(withRouter(withAppStateContext(withCurrentUserContext(Connect(Extension)))))
   extension={
     Object {
       "key": "plugin-key/extension-key",
index 391bb0a7c52c202bdeaf6263af14e0628fd76bbe..52bb015e9171f25e6c051b597ef818bd5256a179 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import withIndexationContext, {
   WithIndexationContextProps
 } from '../../../components/hoc/withIndexationContext';
-import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { hasGlobalPermission } from '../../../helpers/users';
 import { IndexationNotificationType } from '../../../types/indexation';
 import { Permissions } from '../../../types/permissions';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
+import withCurrentUserContext from '../current-user/withCurrentUserContext';
 import './IndexationNotification.css';
 import IndexationNotificationHelper from './IndexationNotificationHelper';
 import IndexationNotificationRenderer from './IndexationNotificationRenderer';
@@ -104,4 +104,4 @@ export class IndexationNotification extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(withIndexationContext(IndexationNotification));
+export default withCurrentUserContext(withIndexationContext(IndexationNotification));
index 7cc1e130c0c1f15568919f5528b0c3b98590dd55..f17925c709b68195e401d3e59eddccd0b289ca2e 100644 (file)
  */
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
 import Favorite from '../../../../components/controls/Favorite';
-import { isLoggedIn } from '../../../../helpers/users';
-import { getCurrentUser, Store } from '../../../../store/rootReducer';
 import { ProjectAlmBindingResponse } from '../../../../types/alm-settings';
 import { BranchLike } from '../../../../types/branch-like';
-import { Component, CurrentUser } from '../../../../types/types';
+import { Component } from '../../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../../types/users';
+import withCurrentUserContext from '../../current-user/withCurrentUserContext';
 import BranchLikeNavigation from './branch-like/BranchLikeNavigation';
 import CurrentBranchLikeMergeInformation from './branch-like/CurrentBranchLikeMergeInformation';
 import { Breadcrumb } from './Breadcrumb';
@@ -70,8 +69,4 @@ export function Header(props: HeaderProps) {
   );
 }
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(React.memo(Header));
+export default withCurrentUserContext(React.memo(Header));
index cf2f755debb9e958e04e6bed8427638800098dc8..f77c4429974016ed94f2e86aee22b819fc2dc332 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { connect } from 'react-redux';
 import BranchStatus from '../../../../components/common/BranchStatus';
 import HomePageSelect from '../../../../components/controls/HomePageSelect';
 import DetachIcon from '../../../../components/icons/DetachIcon';
 import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
 import { isBranch, isPullRequest } from '../../../../helpers/branch-like';
 import { translate } from '../../../../helpers/l10n';
-import { isLoggedIn } from '../../../../helpers/users';
-import { getCurrentUser, Store } from '../../../../store/rootReducer';
 import { BranchLike } from '../../../../types/branch-like';
 import { ComponentQualifier } from '../../../../types/component';
 import { TaskWarning } from '../../../../types/tasks';
-import { Component, CurrentUser, HomePage } from '../../../../types/types';
+import { Component } from '../../../../types/types';
+import { CurrentUser, HomePage, isLoggedIn } from '../../../../types/users';
+import withCurrentUserContext from '../../current-user/withCurrentUserContext';
 import ComponentNavWarnings from './ComponentNavWarnings';
 import './HeaderMeta.css';
 
@@ -124,8 +123,4 @@ export function getCurrentPage(component: Component, branchLike: BranchLike | un
   return currentPage;
 }
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(HeaderMeta);
+export default withCurrentUserContext(HeaderMeta);
index 0a372d2d7b805fc189132b694ea5772768a55db8..02a675e164a33d7b083246563d06cbb10514bf6d 100644 (file)
@@ -9,7 +9,7 @@ exports[`renders correctly: default 1`] = `
   <div
     className="display-flex-center display-flex-space-between little-padded-top padded-bottom"
   >
-    <Connect(Component)
+    <withCurrentUserContext(Component)
       branchLikes={Array []}
       component={
         Object {
@@ -40,7 +40,7 @@ exports[`renders correctly: default 1`] = `
         }
       }
     />
-    <Connect(HeaderMeta)
+    <withCurrentUserContext(HeaderMeta)
       component={
         Object {
           "breadcrumbs": Array [
@@ -112,7 +112,7 @@ exports[`renders correctly: default 1`] = `
     onClose={[Function]}
     top={120}
   >
-    <Connect(withMetricsContext(ProjectInformation))
+    <withCurrentUserContext(withMetricsContext(ProjectInformation))
       component={
         Object {
           "breadcrumbs": Array [
@@ -203,7 +203,7 @@ exports[`renders correctly: has failed notification 1`] = `
   <div
     className="display-flex-center display-flex-space-between little-padded-top padded-bottom"
   >
-    <Connect(Component)
+    <withCurrentUserContext(Component)
       branchLikes={Array []}
       component={
         Object {
@@ -234,7 +234,7 @@ exports[`renders correctly: has failed notification 1`] = `
         }
       }
     />
-    <Connect(HeaderMeta)
+    <withCurrentUserContext(HeaderMeta)
       component={
         Object {
           "breadcrumbs": Array [
@@ -306,7 +306,7 @@ exports[`renders correctly: has failed notification 1`] = `
     onClose={[Function]}
     top={120}
   >
-    <Connect(withMetricsContext(ProjectInformation))
+    <withCurrentUserContext(withMetricsContext(ProjectInformation))
       component={
         Object {
           "breadcrumbs": Array [
@@ -383,7 +383,7 @@ exports[`renders correctly: has failed project binding 1`] = `
   <div
     className="display-flex-center display-flex-space-between little-padded-top padded-bottom"
   >
-    <Connect(Component)
+    <withCurrentUserContext(Component)
       branchLikes={Array []}
       component={
         Object {
@@ -414,7 +414,7 @@ exports[`renders correctly: has failed project binding 1`] = `
         }
       }
     />
-    <Connect(HeaderMeta)
+    <withCurrentUserContext(HeaderMeta)
       component={
         Object {
           "breadcrumbs": Array [
@@ -486,7 +486,7 @@ exports[`renders correctly: has failed project binding 1`] = `
     onClose={[Function]}
     top={120}
   >
-    <Connect(withMetricsContext(ProjectInformation))
+    <withCurrentUserContext(withMetricsContext(ProjectInformation))
       component={
         Object {
           "breadcrumbs": Array [
@@ -565,7 +565,7 @@ exports[`renders correctly: has in progress notification 1`] = `
   <div
     className="display-flex-center display-flex-space-between little-padded-top padded-bottom"
   >
-    <Connect(Component)
+    <withCurrentUserContext(Component)
       branchLikes={Array []}
       component={
         Object {
@@ -596,7 +596,7 @@ exports[`renders correctly: has in progress notification 1`] = `
         }
       }
     />
-    <Connect(HeaderMeta)
+    <withCurrentUserContext(HeaderMeta)
       component={
         Object {
           "breadcrumbs": Array [
@@ -668,7 +668,7 @@ exports[`renders correctly: has in progress notification 1`] = `
     onClose={[Function]}
     top={120}
   >
-    <Connect(withMetricsContext(ProjectInformation))
+    <withCurrentUserContext(withMetricsContext(ProjectInformation))
       component={
         Object {
           "breadcrumbs": Array [
@@ -747,7 +747,7 @@ exports[`renders correctly: has pending notification 1`] = `
   <div
     className="display-flex-center display-flex-space-between little-padded-top padded-bottom"
   >
-    <Connect(Component)
+    <withCurrentUserContext(Component)
       branchLikes={Array []}
       component={
         Object {
@@ -778,7 +778,7 @@ exports[`renders correctly: has pending notification 1`] = `
         }
       }
     />
-    <Connect(HeaderMeta)
+    <withCurrentUserContext(HeaderMeta)
       component={
         Object {
           "breadcrumbs": Array [
@@ -850,7 +850,7 @@ exports[`renders correctly: has pending notification 1`] = `
     onClose={[Function]}
     top={120}
   >
-    <Connect(withMetricsContext(ProjectInformation))
+    <withCurrentUserContext(withMetricsContext(ProjectInformation))
       component={
         Object {
           "breadcrumbs": Array [
@@ -894,7 +894,7 @@ exports[`renders correctly: has warnings 1`] = `
   <div
     className="display-flex-center display-flex-space-between little-padded-top"
   >
-    <Connect(Component)
+    <withCurrentUserContext(Component)
       branchLikes={Array []}
       component={
         Object {
@@ -925,7 +925,7 @@ exports[`renders correctly: has warnings 1`] = `
         }
       }
     />
-    <Connect(HeaderMeta)
+    <withCurrentUserContext(HeaderMeta)
       component={
         Object {
           "breadcrumbs": Array [
@@ -1005,7 +1005,7 @@ exports[`renders correctly: has warnings 1`] = `
     onClose={[Function]}
     top={120}
   >
-    <Connect(withMetricsContext(ProjectInformation))
+    <withCurrentUserContext(withMetricsContext(ProjectInformation))
       component={
         Object {
           "breadcrumbs": Array [
index fd0e0ab897d714cf36eac286f8a3f766d6b2371a..14a257a1b3c92b75b6c0a7efa39f04dee215a491 100644 (file)
@@ -39,7 +39,7 @@ exports[`should render correctly for a branch 1`] = `
     >
       version 0.0.1
     </span>
-    <Connect(HomePageSelect)
+    <withCurrentUserContext(HomePageSelect)
       className="spacer-left"
       currentPage={
         Object {
@@ -92,7 +92,7 @@ exports[`should render correctly for a main project branch 1`] = `
     >
       version 0.0.1
     </span>
-    <Connect(HomePageSelect)
+    <withCurrentUserContext(HomePageSelect)
       className="spacer-left"
       currentPage={
         Object {
@@ -133,7 +133,7 @@ exports[`should render correctly for a portfolio 1`] = `
         }
       />
     </span>
-    <Connect(HomePageSelect)
+    <withCurrentUserContext(HomePageSelect)
       className="spacer-left"
       currentPage={
         Object {
index 6f28bae66978862157035a47067221c8d5b013e7..952b65e7d1ec8328d3d218875c6feb23ff0cd067 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { connect } from 'react-redux';
 import { getMeasures } from '../../../../../api/measures';
-import { isLoggedIn } from '../../../../../helpers/users';
-import { getCurrentUser, Store } from '../../../../../store/rootReducer';
 import { BranchLike } from '../../../../../types/branch-like';
 import { ComponentQualifier } from '../../../../../types/component';
 import { MetricKey } from '../../../../../types/metrics';
-import { Component, CurrentUser, Dict, Measure, Metric } from '../../../../../types/types';
+import { Component, Dict, Measure, Metric } from '../../../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../../../types/users';
+import withCurrentUserContext from '../../../current-user/withCurrentUserContext';
 import withMetricsContext from '../../../metrics/withMetricsContext';
 import ProjectBadges from './badges/ProjectBadges';
 import InfoDrawerPage from './InfoDrawerPage';
@@ -121,8 +120,4 @@ export class ProjectInformation extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(withMetricsContext(ProjectInformation));
+export default withCurrentUserContext(withMetricsContext(ProjectInformation));
index 488135ebd8b1566c7077ff36615afbaa52aa3bda..45288c9ef078a6ae2f3962a37aebe79dffb44d68 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { connect } from 'react-redux';
 import NavBar from '../../../../components/ui/NavBar';
-import { getCurrentUser, Store } from '../../../../store/rootReducer';
-import { CurrentUser } from '../../../../types/types';
+import { CurrentUser } from '../../../../types/users';
 import { rawSizes } from '../../../theme';
+import withCurrentUserContext from '../../current-user/withCurrentUserContext';
 import EmbedDocsPopupHelper from '../../embed-docs-modal/EmbedDocsPopupHelper';
 import Search from '../../search/Search';
 import './GlobalNav.css';
@@ -52,10 +51,4 @@ export function GlobalNav(props: GlobalNavProps) {
   );
 }
 
-const mapStateToProps = (state: Store) => {
-  return {
-    currentUser: getCurrentUser(state)
-  };
-};
-
-export default connect(mapStateToProps)(GlobalNav);
+export default withCurrentUserContext(GlobalNav);
index 230bdffe274e5597d00c92fa26c4a451bcd46d86..e608efc5955eb15b835a8863bbc4ea00f50659d6 100644 (file)
@@ -27,7 +27,8 @@ import { translate } from '../../../../helpers/l10n';
 import { getQualityGatesUrl } from '../../../../helpers/urls';
 import { AppState } from '../../../../types/appstate';
 import { ComponentQualifier } from '../../../../types/component';
-import { CurrentUser, Extension } from '../../../../types/types';
+import { Extension } from '../../../../types/types';
+import { CurrentUser } from '../../../../types/users';
 import withAppStateContext from '../../app-state/withAppStateContext';
 
 interface Props {
index 2e6e24706b4984681e22a58710d67aa4ca2b0a53..91079564dbe37eaec024be83323eaf83123519ba 100644 (file)
@@ -24,8 +24,7 @@ import { Router, withRouter } from '../../../../components/hoc/withRouter';
 import Avatar from '../../../../components/ui/Avatar';
 import { translate } from '../../../../helpers/l10n';
 import { getBaseUrl } from '../../../../helpers/system';
-import { isLoggedIn } from '../../../../helpers/users';
-import { CurrentUser, LoggedInUser } from '../../../../types/types';
+import { CurrentUser, isLoggedIn, LoggedInUser } from '../../../../types/users';
 import { rawSizes } from '../../../theme';
 
 interface Props {
index 8f9e68a3b0118386e69bf0543fa535b4050cac67..924b1da4cae560a88725ab48cbc00dcee5b0331d 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { connect } from 'react-redux';
 import { dismissSonarlintAd } from '../../../api/users';
 import { ButtonLink } from '../../../components/controls/buttons';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import { translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/system';
-import { isLoggedIn } from '../../../helpers/users';
-import { setSonarlintAd } from '../../../store/users';
-import { CurrentUser } from '../../../types/types';
+import { isLoggedIn } from '../../../types/users';
+import { CurrentUserContextInterface } from '../current-user/CurrentUserContext';
+import withCurrentUserContext from '../current-user/withCurrentUserContext';
 import './PromotionNotification.css';
 
-export interface PromotionNotificationProps {
-  setSonarlintAd: () => void;
-  currentUser: CurrentUser;
-}
+export interface PromotionNotificationProps
+  extends Pick<CurrentUserContextInterface, 'currentUser' | 'updateCurrentUserSonarLintAdSeen'> {}
 
 export function PromotionNotification(props: PromotionNotificationProps) {
   const { currentUser } = props;
@@ -43,7 +39,7 @@ export function PromotionNotification(props: PromotionNotificationProps) {
 
   const onClick = () => {
     dismissSonarlintAd();
-    props.setSonarlintAd();
+    props.updateCurrentUserSonarLintAdSeen();
   };
 
   return (
@@ -74,6 +70,4 @@ export function PromotionNotification(props: PromotionNotificationProps) {
   );
 }
 
-const dispatchToProps = { setSonarlintAd };
-
-export default connect(null, dispatchToProps)(withCurrentUser(PromotionNotification));
+export default withCurrentUserContext(PromotionNotification);
index c39dd065ca1ae57ab47756c5bc85358a6d2ad9b9..059d0560dbfbbd2cf7fa0f980614c107d26b6741 100644 (file)
@@ -40,29 +40,33 @@ it('should render correctly', () => {
 });
 
 it('should remove the toaster when click on dismiss', () => {
-  const setSonarlintAd = jest.fn();
+  const updateCurrentUserSonarLintAdSeen = jest.fn();
   const wrapper = shallowRender({
     currentUser: mockLoggedInUser({ sonarLintAdSeen: false }),
-    setSonarlintAd
+    updateCurrentUserSonarLintAdSeen
   });
   wrapper.find('.toaster-actions ButtonLink').simulate('click');
   expect(dismissSonarlintAd).toBeCalled();
-  expect(setSonarlintAd).toBeCalled();
+  expect(updateCurrentUserSonarLintAdSeen).toBeCalled();
 });
 
 it('should remove the toaster and navigate to sonarlint when click on learn more', () => {
-  const setSonarlintAd = jest.fn();
+  const updateCurrentUserSonarLintAdSeen = jest.fn();
   const wrapper = shallowRender({
     currentUser: mockLoggedInUser({ sonarLintAdSeen: false }),
-    setSonarlintAd
+    updateCurrentUserSonarLintAdSeen
   });
   wrapper.find('.toaster-actions .button-primary').simulate('click');
   expect(dismissSonarlintAd).toBeCalled();
-  expect(setSonarlintAd).toBeCalled();
+  expect(updateCurrentUserSonarLintAdSeen).toBeCalled();
 });
 
 function shallowRender(props: Partial<PromotionNotificationProps> = {}) {
   return shallow(
-    <PromotionNotification currentUser={mockCurrentUser()} setSonarlintAd={jest.fn()} {...props} />
+    <PromotionNotification
+      currentUser={mockCurrentUser()}
+      updateCurrentUserSonarLintAdSeen={jest.fn()}
+      {...props}
+    />
   );
 }
index 0140d17e0b158a7545cab3eaf32e281d459f8e94..9e8664ef0fc1d2b6e3326b8f6858af460109decb 100644 (file)
@@ -34,7 +34,8 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { scrollToElement } from '../../../helpers/scrolling';
 import { getComponentOverviewUrl } from '../../../helpers/urls';
 import { ComponentQualifier } from '../../../types/component';
-import { CurrentUser, Dict } from '../../../types/types';
+import { Dict } from '../../../types/types';
+import { CurrentUser } from '../../../types/users';
 import RecentHistory from '../RecentHistory';
 import './Search.css';
 import { ComponentResult, More, Results, sortQualifiers } from './utils';
index b399276ddf045d11e38c3931ed836ae405db409d..123260509daee0805d8ec3369c277bbde5ff6753 100644 (file)
 import { groupBy, isEmpty, mapValues } from 'lodash';
 import * as React from 'react';
 import { getSystemUpgrades } from '../../../api/system';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import { Alert, AlertVariant } from '../../../components/ui/Alert';
 import DismissableAlert from '../../../components/ui/DismissableAlert';
 import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton';
 import { sortUpgrades, UpdateUseCase } from '../../../components/upgrade/utils';
 import { translate } from '../../../helpers/l10n';
-import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { hasGlobalPermission } from '../../../helpers/users';
 import { AppState } from '../../../types/appstate';
 import { Permissions } from '../../../types/permissions';
 import { SystemUpgrade } from '../../../types/system';
-import { CurrentUser, Dict } from '../../../types/types';
+import { Dict } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import withAppStateContext from '../app-state/withAppStateContext';
+import withCurrentUserContext from '../current-user/withCurrentUserContext';
 import './UpdateNotification.css';
 
 const MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION = 6;
@@ -74,20 +75,12 @@ export class UpdateNotification extends React.PureComponent<Props, State> {
       canSeeNotification: false,
       useCase: UpdateUseCase.NewMinorVersion
     };
-    this.fetchSystemUpgradeInformation();
   }
 
   componentDidMount() {
     this.mounted = true;
-  }
 
-  componentDidUpdate(prevProps: Props) {
-    if (
-      prevProps.currentUser !== this.props.currentUser ||
-      this.props.appState.version !== prevProps.appState.version
-    ) {
-      this.fetchSystemUpgradeInformation();
-    }
+    this.fetchSystemUpgradeInformation();
   }
 
   componentWillUnmount() {
@@ -250,4 +243,4 @@ export class UpdateNotification extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(withAppStateContext(UpdateNotification));
+export default withCurrentUserContext(withAppStateContext(UpdateNotification));
index b5d9edd0b3be0e4383283cf85dfcad50462a7c42..f63a4aee7f74f3449369c29b9e363cb0876882f1 100644 (file)
@@ -42,13 +42,18 @@ function formatDate(date: Date): string {
 }
 
 it('should render correctly', async () => {
-  const wrapper = shallowRender({
+  let wrapper = shallowRender({
     appState: mockAppState({ version: '9.0' }),
     currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
   });
   await waitAndUpdate(wrapper);
   expect(wrapper).toMatchSnapshot('default');
-  expect(wrapper.setProps({ currentUser: mockCurrentUser() })).toMatchSnapshot('anonymous user');
+
+  wrapper = shallowRender({
+    appState: mockAppState({ version: '9.0' }),
+    currentUser: mockCurrentUser()
+  });
+  expect(wrapper.type()).toBeNull();
 });
 
 it('should not show prompt when not admin', async () => {
index c788a784593919caa4da2a33d40e99812dce706d..a6d9f0767dde1b9ca8f9d17ec0001a9b0bf06699 100644 (file)
@@ -1,7 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should render correctly: anonymous user 1`] = `""`;
-
 exports[`should render correctly: default 1`] = `
 <DismissableAlert
   alertKey="new_minor_version9.1"
index 63b5701d0d10d2b2980d392eeeebfceb8591d86b..251dd073983f1c383e3c881e35c6d796ef3471ef 100644 (file)
  */
 import { Store } from 'redux';
 import rootReducer, { Store as State } from '../../store/rootReducer';
-import { receiveCurrentUser } from '../../store/users';
 import configureStore from '../../store/utils/configureStore';
-import { CurrentUser } from '../../types/types';
 
 let store: Store<State, any>;
 
-const createStore = (currentUser?: CurrentUser) => {
+const createStore = () => {
   store = configureStore(rootReducer);
-  if (currentUser) {
-    store.dispatch(receiveCurrentUser(currentUser));
-  }
   return store;
 };
 
-export default (currentUser?: CurrentUser) => (store ? store : createStore(currentUser));
+export default () => (store ? store : createStore());
index 6854fa5fbb75e1c990e5a3feb20fb8595478955c..0c8dc2ab1d500de289696ad8113eb55a138b7f32 100644 (file)
@@ -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.
  */
-/* eslint-disable react/jsx-sort-props */
+
 import { Location } from 'history';
 import { pick } from 'lodash';
 import * as React from 'react';
@@ -61,9 +61,10 @@ import withIndexationGuard from '../../components/hoc/withIndexationGuard';
 import { lazyLoadComponent } from '../../components/lazyLoadComponent';
 import getHistory from '../../helpers/getHistory';
 import { AppState } from '../../types/appstate';
-import { CurrentUser } from '../../types/types';
+import { CurrentUser } from '../../types/users';
 import App from '../components/App';
 import AppStateContextProvider from '../components/app-state/AppStateContextProvider';
+import CurrentUserContextProvider from '../components/current-user/CurrentUserContextProvider';
 import GlobalContainer from '../components/GlobalContainer';
 import { PageContext } from '../components/indexation/PageUnavailableDueToIndexation';
 import MigrationContainer from '../components/MigrationContainer';
@@ -286,100 +287,105 @@ export default function startReactApp(lang: string, appState: AppState, currentU
   const el = document.getElementById('content');
 
   const history = getHistory();
-  const store = getStore(currentUser);
+  const store = getStore();
 
   render(
     <HelmetProvider>
       <Provider store={store}>
         <AppStateContextProvider appState={appState}>
-          <IntlProvider defaultLocale={lang} locale={lang}>
-            <Router history={history} onUpdate={handleUpdate}>
-              {renderRedirects()}
-
-              <Route
-                path="formatting/help"
-                component={lazyLoadComponent(() => import('../components/FormattingHelp'))}
-              />
+          <CurrentUserContextProvider currentUser={currentUser}>
+            <IntlProvider defaultLocale={lang} locale={lang}>
+              <Router history={history} onUpdate={handleUpdate}>
+                {renderRedirects()}
 
-              <Route component={lazyLoadComponent(() => import('../components/SimpleContainer'))}>
-                <Route path="maintenance">{maintenanceRoutes}</Route>
-                <Route path="setup">{setupRoutes}</Route>
-              </Route>
-
-              <Route component={MigrationContainer}>
                 <Route
-                  component={lazyLoadComponent(() =>
-                    import('../components/SimpleSessionsContainer')
-                  )}>
-                  <RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} />
+                  path="formatting/help"
+                  component={lazyLoadComponent(() => import('../components/FormattingHelp'))}
+                />
+
+                <Route component={lazyLoadComponent(() => import('../components/SimpleContainer'))}>
+                  <Route path="maintenance">{maintenanceRoutes}</Route>
+                  <Route path="setup">{setupRoutes}</Route>
                 </Route>
 
-                <Route path="/" component={App}>
-                  <IndexRoute
-                    component={lazyLoadComponent(() => import('../components/Landing'))}
-                  />
+                <Route component={MigrationContainer}>
+                  <Route
+                    component={lazyLoadComponent(() =>
+                      import('../components/SimpleSessionsContainer')
+                    )}>
+                    <RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} />
+                  </Route>
 
-                  <Route component={GlobalContainer}>
-                    <RouteWithChildRoutes path="account" childRoutes={accountRoutes} />
-                    <RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
-                    <RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} />
+                  <Route path="/" component={App}>
+                    <IndexRoute
+                      component={lazyLoadComponent(() => import('../components/Landing'))}
+                    />
+
+                    <Route component={GlobalContainer}>
+                      <RouteWithChildRoutes path="account" childRoutes={accountRoutes} />
+                      <RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
+                      <RouteWithChildRoutes
+                        path="documentation"
+                        childRoutes={documentationRoutes}
+                      />
+                      <Route
+                        path="extension/:pluginKey/:extensionKey"
+                        component={lazyLoadComponent(() =>
+                          import('../components/extensions/GlobalPageExtension')
+                        )}
+                      />
+                      <Route
+                        path="issues"
+                        component={withIndexationGuard(Issues, PageContext.Issues)}
+                      />
+                      <RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
+                      <RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />
+                      <Route
+                        path="portfolios"
+                        component={lazyLoadComponent(() =>
+                          import('../components/extensions/PortfoliosPage')
+                        )}
+                      />
+                      <RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} />
+                      <RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} />
+
+                      {renderComponentRoutes()}
+
+                      {renderAdminRoutes()}
+                    </Route>
+                    <Route
+                      // We don't want this route to have any menu.
+                      // That is why we can not have it under the accountRoutes
+                      path="account/reset_password"
+                      component={lazyLoadComponent(() => import('../components/ResetPassword'))}
+                    />
                     <Route
-                      path="extension/:pluginKey/:extensionKey"
+                      // We don't want this route to have any menu. This is why we define it here
+                      // rather than under the admin routes.
+                      path="admin/change_admin_password"
                       component={lazyLoadComponent(() =>
-                        import('../components/extensions/GlobalPageExtension')
+                        import('../../apps/change-admin-password/ChangeAdminPasswordApp')
                       )}
                     />
                     <Route
-                      path="issues"
-                      component={withIndexationGuard(Issues, PageContext.Issues)}
+                      // We don't want this route to have any menu. This is why we define it here
+                      // rather than under the admin routes.
+                      path="admin/plugin_risk_consent"
+                      component={lazyLoadComponent(() => import('../components/PluginRiskConsent'))}
                     />
-                    <RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
-                    <RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />
                     <Route
-                      path="portfolios"
-                      component={lazyLoadComponent(() =>
-                        import('../components/extensions/PortfoliosPage')
-                      )}
+                      path="not_found"
+                      component={lazyLoadComponent(() => import('../components/NotFound'))}
+                    />
+                    <Route
+                      path="*"
+                      component={lazyLoadComponent(() => import('../components/NotFound'))}
                     />
-                    <RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} />
-                    <RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} />
-
-                    {renderComponentRoutes()}
-
-                    {renderAdminRoutes()}
                   </Route>
-                  <Route
-                    // We don't want this route to have any menu.
-                    // That is why we can not have it under the accountRoutes
-                    path="account/reset_password"
-                    component={lazyLoadComponent(() => import('../components/ResetPassword'))}
-                  />
-                  <Route
-                    // We don't want this route to have any menu. This is why we define it here
-                    // rather than under the admin routes.
-                    path="admin/change_admin_password"
-                    component={lazyLoadComponent(() =>
-                      import('../../apps/change-admin-password/ChangeAdminPasswordApp')
-                    )}
-                  />
-                  <Route
-                    // We don't want this route to have any menu. This is why we define it here
-                    // rather than under the admin routes.
-                    path="admin/plugin_risk_consent"
-                    component={lazyLoadComponent(() => import('../components/PluginRiskConsent'))}
-                  />
-                  <Route
-                    path="not_found"
-                    component={lazyLoadComponent(() => import('../components/NotFound'))}
-                  />
-                  <Route
-                    path="*"
-                    component={lazyLoadComponent(() => import('../components/NotFound'))}
-                  />
                 </Route>
-              </Route>
-            </Router>
-          </IntlProvider>
+              </Router>
+            </IntlProvider>
+          </CurrentUserContextProvider>
         </AppStateContextProvider>
       </Provider>
     </HelmetProvider>,
index 88741f793a479ffee1ed7f80584db69998a0443b..672f3fa3da8cc1bf5bdd541fc268636543b0c9b0 100644 (file)
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication';
 import { translate } from '../../../helpers/l10n';
-import { CurrentUser, LoggedInUser } from '../../../types/types';
+import { CurrentUser, LoggedInUser } from '../../../types/users';
 import '../account.css';
 import Nav from './Nav';
 import UserCard from './UserCard';
@@ -66,4 +66,4 @@ export class Account extends React.PureComponent<Props> {
   }
 }
 
-export default withCurrentUser(Account);
+export default withCurrentUserContext(Account);
index 00706eb648925a3e663205ef297ee3cc29206fb3..1cc827ceeea3d4c513e0a1301f34ccbfec6994d1 100644 (file)
  */
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import ResetPasswordForm from '../../../components/common/ResetPasswordForm';
 import { translate } from '../../../helpers/l10n';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import Tokens from './Tokens';
 
 export interface SecurityProps {
-  user: LoggedInUser;
+  currentUser: LoggedInUser;
 }
 
-export function Security({ user }: SecurityProps) {
+export function Security({ currentUser }: SecurityProps) {
   return (
     <div className="account-body account-container">
       <Helmet defer={false} title={translate('my_account.security')} />
-      <Tokens login={user.login} />
-      {user.local && (
+      <Tokens login={currentUser.login} />
+      {currentUser.local && (
         <section className="boxed-group">
           <h2 className="spacer-bottom">{translate('my_profile.password.title')}</h2>
-          <ResetPasswordForm className="boxed-group-inner" user={user} />
+          <ResetPasswordForm className="boxed-group-inner" user={currentUser} />
         </section>
       )}
     </div>
   );
 }
 
-const mapStateToProps = (state: Store) => ({
-  user: getCurrentUser(state) as LoggedInUser
-});
-
-export default connect(mapStateToProps)(Security);
+export default withCurrentUserContext(Security);
index 4f73fcdd974a33fbf558120b079fe871001de151..e53c2d39c4852c5614c2c67ed5df42f6da303f74 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import Avatar from '../../../components/ui/Avatar';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 
 interface Props {
   user: LoggedInUser;
index 2420ac91bc3ba48e31fa43a44423410031f92cee..30c1668fe1e5a5d2bca7f08114853323e561868f 100644 (file)
@@ -24,11 +24,11 @@ import { Security, SecurityProps } from '../Security';
 
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot('local user');
-  expect(shallowRender({ user: mockLoggedInUser({ local: false }) })).toMatchSnapshot(
+  expect(shallowRender({ currentUser: mockLoggedInUser({ local: false }) })).toMatchSnapshot(
     'non-local user'
   );
 });
 
 function shallowRender(props: Partial<SecurityProps> = {}) {
-  return shallow(<Security user={mockLoggedInUser({ local: true })} {...props} />);
+  return shallow(<Security currentUser={mockLoggedInUser({ local: true })} {...props} />);
 }
index d8432f831694ab5e06590e5b78504a48be4b0723..c6dd5a8d5e26b94ec5b5c0a11c44076767af53f7 100644 (file)
  */
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
-import { isSonarCloud } from '../../../helpers/system';
 import { Notification } from '../../../types/types';
 import NotificationsList from './NotificationsList';
-import SonarCloudNotifications from './SonarCloudNotifications';
 
 interface Props {
   addNotification: (n: Notification) => void;
@@ -34,36 +32,33 @@ interface Props {
 
 export default function GlobalNotifications(props: Props) {
   return (
-    <>
-      <section className="boxed-group">
-        <h2>{translate('my_profile.overall_notifications.title')}</h2>
+    <section className="boxed-group">
+      <h2>{translate('my_profile.overall_notifications.title')}</h2>
 
-        <div className="boxed-group-inner">
-          <table className="data zebra">
-            <thead>
-              <tr>
-                <th />
-                {props.channels.map(channel => (
-                  <th className="text-center" key={channel}>
-                    <h4>{translate('notification.channel', channel)}</h4>
-                  </th>
-                ))}
-              </tr>
-            </thead>
+      <div className="boxed-group-inner">
+        <table className="data zebra">
+          <thead>
+            <tr>
+              <th />
+              {props.channels.map(channel => (
+                <th className="text-center" key={channel}>
+                  <h4>{translate('notification.channel', channel)}</h4>
+                </th>
+              ))}
+            </tr>
+          </thead>
 
-            <NotificationsList
-              channels={props.channels}
-              checkboxId={getCheckboxId}
-              notifications={props.notifications}
-              onAdd={props.addNotification}
-              onRemove={props.removeNotification}
-              types={props.types}
-            />
-          </table>
-        </div>
-      </section>
-      {isSonarCloud() && <SonarCloudNotifications />}
-    </>
+          <NotificationsList
+            channels={props.channels}
+            checkboxId={getCheckboxId}
+            notifications={props.notifications}
+            onAdd={props.addNotification}
+            onRemove={props.removeNotification}
+            types={props.types}
+          />
+        </table>
+      </div>
+    </section>
   );
 }
 
diff --git a/server/sonar-web/src/main/js/apps/account/notifications/SonarCloudNotifications.tsx b/server/sonar-web/src/main/js/apps/account/notifications/SonarCloudNotifications.tsx
deleted file mode 100644 (file)
index b9ab370..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { connect } from 'react-redux';
-import Checkbox from '../../../components/controls/Checkbox';
-import { translate } from '../../../helpers/l10n';
-import { getCurrentUserSetting, Store } from '../../../store/rootReducer';
-import { setCurrentUserSetting } from '../../../store/users';
-import { CurrentUserSetting } from '../../../types/types';
-
-interface Props {
-  notificationsOptOut?: boolean;
-  setCurrentUserSetting: (setting: CurrentUserSetting) => void;
-}
-
-export class SonarCloudNotifications extends React.PureComponent<Props> {
-  handleCheckOptOut = (checked: boolean) => {
-    this.props.setCurrentUserSetting({
-      key: 'notifications.optOut',
-      value: checked ? 'false' : 'true'
-    });
-  };
-
-  render() {
-    return (
-      <section className="boxed-group">
-        <h2>{translate('my_profile.sonarcloud_feature_notifications.title')}</h2>
-        <div className="boxed-group-inner">
-          <table className="data zebra">
-            <thead>
-              <tr>
-                <th />
-                <th className="text-center">
-                  <h4>{translate('activate')}</h4>
-                </th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr>
-                <td>{translate('my_profile.sonarcloud_feature_notifications.description')}</td>
-                <td className="text-center">
-                  <Checkbox
-                    checked={!this.props.notificationsOptOut}
-                    onCheck={this.handleCheckOptOut}
-                  />
-                </td>
-              </tr>
-            </tbody>
-          </table>
-        </div>
-      </section>
-    );
-  }
-}
-
-const mapStateToProps = (state: Store) => {
-  const notificationsOptOut = getCurrentUserSetting(state, 'notifications.optOut') === 'true';
-
-  return {
-    notificationsOptOut
-  };
-};
-
-const mapDispatchToProps = {
-  setCurrentUserSetting
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(SonarCloudNotifications);
diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/SonarCloudNotifications-test.tsx b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/SonarCloudNotifications-test.tsx
deleted file mode 100644 (file)
index 5f39c57..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { SonarCloudNotifications } from '../SonarCloudNotifications';
-
-it('should match snapshot', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<SonarCloudNotifications['props']> = {}) {
-  return shallow(
-    <SonarCloudNotifications
-      notificationsOptOut={true}
-      setCurrentUserSetting={jest.fn()}
-      {...props}
-    />
-  );
-}
index 2eb1e232053879bf01031dd21fbb684c4eb00024..59424c1894dfea72c71962900d48fa1af7806d78 100644 (file)
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should match snapshot 1`] = `
-<Fragment>
-  <section
-    className="boxed-group"
+<section
+  className="boxed-group"
+>
+  <h2>
+    my_profile.overall_notifications.title
+  </h2>
+  <div
+    className="boxed-group-inner"
   >
-    <h2>
-      my_profile.overall_notifications.title
-    </h2>
-    <div
-      className="boxed-group-inner"
+    <table
+      className="data zebra"
     >
-      <table
-        className="data zebra"
-      >
-        <thead>
-          <tr>
-            <th />
-            <th
-              className="text-center"
-              key="channel1"
-            >
-              <h4>
-                notification.channel.channel1
-              </h4>
-            </th>
-            <th
-              className="text-center"
-              key="channel2"
-            >
-              <h4>
-                notification.channel.channel2
-              </h4>
-            </th>
-          </tr>
-        </thead>
-        <NotificationsList
-          channels={
-            Array [
-              "channel1",
-              "channel2",
-            ]
-          }
-          checkboxId={[Function]}
-          notifications={
-            Array [
-              Object {
-                "channel": "channel1",
-                "type": "type1",
-              },
-              Object {
-                "channel": "channel1",
-                "type": "type2",
-              },
-              Object {
-                "channel": "channel2",
-                "type": "type2",
-              },
-            ]
-          }
-          onAdd={[MockFunction]}
-          onRemove={[MockFunction]}
-          types={
-            Array [
-              "type1",
-              "type2",
-            ]
-          }
-        />
-      </table>
-    </div>
-  </section>
-</Fragment>
+      <thead>
+        <tr>
+          <th />
+          <th
+            className="text-center"
+            key="channel1"
+          >
+            <h4>
+              notification.channel.channel1
+            </h4>
+          </th>
+          <th
+            className="text-center"
+            key="channel2"
+          >
+            <h4>
+              notification.channel.channel2
+            </h4>
+          </th>
+        </tr>
+      </thead>
+      <NotificationsList
+        channels={
+          Array [
+            "channel1",
+            "channel2",
+          ]
+        }
+        checkboxId={[Function]}
+        notifications={
+          Array [
+            Object {
+              "channel": "channel1",
+              "type": "type1",
+            },
+            Object {
+              "channel": "channel1",
+              "type": "type2",
+            },
+            Object {
+              "channel": "channel2",
+              "type": "type2",
+            },
+          ]
+        }
+        onAdd={[MockFunction]}
+        onRemove={[MockFunction]}
+        types={
+          Array [
+            "type1",
+            "type2",
+          ]
+        }
+      />
+    </table>
+  </div>
+</section>
 `;
 
 exports[`should show SonarCloud options if in SC context 1`] = `
-<Fragment>
-  <section
-    className="boxed-group"
+<section
+  className="boxed-group"
+>
+  <h2>
+    my_profile.overall_notifications.title
+  </h2>
+  <div
+    className="boxed-group-inner"
   >
-    <h2>
-      my_profile.overall_notifications.title
-    </h2>
-    <div
-      className="boxed-group-inner"
+    <table
+      className="data zebra"
     >
-      <table
-        className="data zebra"
-      >
-        <thead>
-          <tr>
-            <th />
-            <th
-              className="text-center"
-              key="channel1"
-            >
-              <h4>
-                notification.channel.channel1
-              </h4>
-            </th>
-            <th
-              className="text-center"
-              key="channel2"
-            >
-              <h4>
-                notification.channel.channel2
-              </h4>
-            </th>
-          </tr>
-        </thead>
-        <NotificationsList
-          channels={
-            Array [
-              "channel1",
-              "channel2",
-            ]
-          }
-          checkboxId={[Function]}
-          notifications={
-            Array [
-              Object {
-                "channel": "channel1",
-                "type": "type1",
-              },
-              Object {
-                "channel": "channel1",
-                "type": "type2",
-              },
-              Object {
-                "channel": "channel2",
-                "type": "type2",
-              },
-            ]
-          }
-          onAdd={[MockFunction]}
-          onRemove={[MockFunction]}
-          types={
-            Array [
-              "type1",
-              "type2",
-            ]
-          }
-        />
-      </table>
-    </div>
-  </section>
-  <Connect(SonarCloudNotifications) />
-</Fragment>
+      <thead>
+        <tr>
+          <th />
+          <th
+            className="text-center"
+            key="channel1"
+          >
+            <h4>
+              notification.channel.channel1
+            </h4>
+          </th>
+          <th
+            className="text-center"
+            key="channel2"
+          >
+            <h4>
+              notification.channel.channel2
+            </h4>
+          </th>
+        </tr>
+      </thead>
+      <NotificationsList
+        channels={
+          Array [
+            "channel1",
+            "channel2",
+          ]
+        }
+        checkboxId={[Function]}
+        notifications={
+          Array [
+            Object {
+              "channel": "channel1",
+              "type": "type1",
+            },
+            Object {
+              "channel": "channel1",
+              "type": "type2",
+            },
+            Object {
+              "channel": "channel2",
+              "type": "type2",
+            },
+          ]
+        }
+        onAdd={[MockFunction]}
+        onRemove={[MockFunction]}
+        types={
+          Array [
+            "type1",
+            "type2",
+          ]
+        }
+      />
+    </table>
+  </div>
+</section>
 `;
diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/SonarCloudNotifications-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/SonarCloudNotifications-test.tsx.snap
deleted file mode 100644 (file)
index f70cefb..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should match snapshot 1`] = `
-<section
-  className="boxed-group"
->
-  <h2>
-    my_profile.sonarcloud_feature_notifications.title
-  </h2>
-  <div
-    className="boxed-group-inner"
-  >
-    <table
-      className="data zebra"
-    >
-      <thead>
-        <tr>
-          <th />
-          <th
-            className="text-center"
-          >
-            <h4>
-              activate
-            </h4>
-          </th>
-        </tr>
-      </thead>
-      <tbody>
-        <tr>
-          <td>
-            my_profile.sonarcloud_feature_notifications.description
-          </td>
-          <td
-            className="text-center"
-          >
-            <Checkbox
-              checked={false}
-              onCheck={[Function]}
-              thirdState={false}
-            />
-          </td>
-        </tr>
-      </tbody>
-    </table>
-  </div>
-</section>
-`;
index e8d94c041e28cd23c41fe384506bb4114136ecca..45fb5a5ffd540200b6c7baf999174bf048dcd045 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn';
 import { translate } from '../../../helpers/l10n';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import UserExternalIdentity from './UserExternalIdentity';
 
 export interface ProfileProps {
index f6c17d1220188c0e1debd28db4fd7f2f8e8e55d2..c97fb3e6adbd124704e1ea8af2a4d25f7397325e 100644 (file)
@@ -22,7 +22,8 @@ import { getIdentityProviders } from '../../../api/users';
 import { colors } from '../../../app/theme';
 import { getTextColor } from '../../../helpers/colors';
 import { getBaseUrl } from '../../../helpers/system';
-import { IdentityProvider, LoggedInUser } from '../../../types/types';
+import { IdentityProvider } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 
 export interface UserExternalIdentityProps {
   user: LoggedInUser;
index a49dfd37afd83640f208c9b33ed0ebb06d473786..53a3f6da6000cc98dfdb745bfcb8f0008ffaa256 100644 (file)
@@ -21,11 +21,11 @@ import key from 'keymaster';
 import { keyBy } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
 import { withRouter, WithRouterProps } from 'react-router';
 import { Profile, searchQualityProfiles } from '../../../api/quality-profiles';
 import { getRulesApp, searchRules } from '../../../api/rules';
 import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
 import FiltersHeader from '../../../components/common/FiltersHeader';
 import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
@@ -41,10 +41,9 @@ import {
   removeWhitePageClass
 } from '../../../helpers/pages';
 import { scrollToElement } from '../../../helpers/scrolling';
-import { isLoggedIn } from '../../../helpers/users';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
 import { SecurityStandard } from '../../../types/security';
-import { CurrentUser, Dict, Paging, RawQuery, Rule, RuleActivation } from '../../../types/types';
+import { Dict, Paging, RawQuery, Rule, RuleActivation } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import {
   shouldOpenSonarSourceSecurityFacet,
   shouldOpenStandardsChildFacet,
@@ -690,8 +689,4 @@ function parseFacets(rawFacets: { property: string; values: { count: number; val
   return facets;
 }
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default withRouter(connect(mapStateToProps)(App));
+export default withRouter(withCurrentUserContext(App));
index 4c4a3d7bbb6f02456aa2d798e6e38de22f96bb23..1f9706f2148d49b89dcf6f8e6223b83f92acb142 100644 (file)
@@ -28,7 +28,7 @@ import { translate } from '../../../helpers/l10n';
 import { getProjectUrl } from '../../../helpers/urls';
 import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
 import { AppState } from '../../../types/appstate';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import AlmBindingDefinitionForm from '../../settings/components/almIntegration/AlmBindingDefinitionForm';
 import AzureProjectCreate from './AzureProjectCreate';
 import BitbucketCloudProjectCreate from './BitbucketCloudProjectCreate';
index 2423ee9687a8bf12961f5bddeddbd84a0c895116..1e3578afcb163d0c4203be72319a550a5b7e70e4 100644 (file)
  */
 import { connect } from 'react-redux';
 import { searchIssues } from '../../../api/issues';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { withRouter } from '../../../components/hoc/withRouter';
 import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
 import { parseIssueFromResponse } from '../../../helpers/issues';
 import { fetchBranchStatus } from '../../../store/rootActions';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
+import { Store } from '../../../store/rootReducer';
 import { FetchIssuesPromise } from '../../../types/issues';
 import { RawQuery } from '../../../types/types';
 
 const IssuesAppContainer = lazyLoadComponent(() => import('./IssuesApp'), 'IssuesAppContainer');
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state),
-  fetchIssues
-});
-
 const fetchIssues = (query: RawQuery) => {
   return searchIssues({
     ...query,
@@ -47,6 +43,12 @@ const fetchIssues = (query: RawQuery) => {
   });
 };
 
+const mapStateToProps = (_state: Store) => ({
+  fetchIssues
+});
+
 const mapDispatchToProps = { fetchBranchStatus };
 
-export default withRouter(connect(mapStateToProps, mapDispatchToProps)(IssuesAppContainer));
+export default withRouter(
+  withCurrentUserContext(connect(mapStateToProps, mapDispatchToProps)(IssuesAppContainer))
+);
index 52b4dc306b81cd1756e64d577c1a3b5d1b25e93d..fe7516661181cb6944540071462b6c781512461b 100644 (file)
@@ -36,8 +36,8 @@ import SeverityHelper from '../../../components/shared/SeverityHelper';
 import { Alert } from '../../../components/ui/Alert';
 import Avatar from '../../../components/ui/Avatar';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { isLoggedIn, isUserActive } from '../../../helpers/users';
-import { Component, CurrentUser, Dict, Issue, IssueType, Paging } from '../../../types/types';
+import { Component, Dict, Issue, IssueType, Paging } from '../../../types/types';
+import { CurrentUser, isLoggedIn, isUserActive } from '../../../types/users';
 import { searchAssignees } from '../utils';
 
 interface AssigneeOption {
index 60a57ed3c92cdc3e2b07e505c396b143f8146561..848b4da99fe08782f63874eedad856b82d811478 100644 (file)
@@ -62,15 +62,8 @@ import {
   ReferencedRule
 } from '../../../types/issues';
 import { SecurityStandard } from '../../../types/security';
-import {
-  Component,
-  CurrentUser,
-  Dict,
-  Issue,
-  Paging,
-  RawQuery,
-  UserBase
-} from '../../../types/types';
+import { Component, Dict, Issue, Paging, RawQuery } from '../../../types/types';
+import { CurrentUser, UserBase } from '../../../types/users';
 import * as actions from '../actions';
 import ConciseIssuesList from '../conciseIssuesList/ConciseIssuesList';
 import ConciseIssuesListHeader from '../conciseIssuesList/ConciseIssuesListHeader';
index 84b7426c79b1f8031100050c0246347b3a892d9c..b1543639017a8f9b779be69dbfc21fe9f31b8e48 100644 (file)
@@ -19,7 +19,6 @@
  */
 import { connect } from 'react-redux';
 import { searchIssues } from '../../../../api/issues';
-import { mockCurrentUser } from '../../../../helpers/testMocks';
 import { fetchBranchStatus } from '../../../../store/rootActions';
 import '../AppContainer';
 
@@ -35,19 +34,11 @@ jest.mock('../../../../helpers/issues', () => ({
   parseIssueFromResponse: jest.fn(() => 'parsedIssue')
 }));
 
-jest.mock('../../../../store/rootReducer', () => {
-  const { mockCurrentUser } = jest.requireActual('../../../../helpers/testMocks');
-  return {
-    getCurrentUser: jest.fn(() => mockCurrentUser())
-  };
-});
-
 describe('redux', () => {
   it('should correctly map state and dispatch props', async () => {
     const [mapStateToProps, mapDispatchToProps] = (connect as jest.Mock).mock.calls[0];
-    const { currentUser, fetchIssues } = mapStateToProps({});
+    const { fetchIssues } = mapStateToProps({});
 
-    expect(currentUser).toEqual(mockCurrentUser());
     expect(mapDispatchToProps).toEqual(expect.objectContaining({ fetchBranchStatus }));
 
     const result = await fetchIssues({ foo: 'bar' });
index 7424582257abe6090ceb1a681164d243c3526671..1f7f77d4a93ee6d7838012f49cb8c922f37a62e8 100644 (file)
@@ -15,7 +15,7 @@ exports[`should render 1`] = `
       effort={125}
     />
   </div>
-  <Connect(HomePageSelect)
+  <withCurrentUserContext(HomePageSelect)
     className="huge-spacer-left"
     currentPage={
       Object {
index 7a9c8a49ae27ce11b67cf96da228665a20ddef33..9412a6395f775ba55227920bdd72e5a6e9f56a1e 100644 (file)
@@ -23,9 +23,9 @@ import ListStyleFacet from '../../../components/facet/ListStyleFacet';
 import Avatar from '../../../components/ui/Avatar';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { highlightTerm } from '../../../helpers/search';
-import { isUserActive } from '../../../helpers/users';
 import { Facet } from '../../../types/issues';
-import { Dict, UserBase } from '../../../types/types';
+import { Dict } from '../../../types/types';
+import { isUserActive, UserBase } from '../../../types/users';
 import { Query, searchAssignees } from '../utils';
 
 interface Props {
index 4a80bd6dd9631c0854848f6ab78e4e1eb677ac3e..e15af101eacf23456b71d884172ce13afec0283d 100644 (file)
@@ -30,7 +30,8 @@ import {
   ReferencedRule
 } from '../../../types/issues';
 import { GlobalSettingKeys } from '../../../types/settings';
-import { Component, Dict, UserBase } from '../../../types/types';
+import { Component, Dict } from '../../../types/types';
+import { UserBase } from '../../../types/users';
 import { Query } from '../utils';
 import AssigneeFacet from './AssigneeFacet';
 import AuthorFacet from './AuthorFacet';
index b022a30dc8bb887136586e915595eb1ac836756f..7b478ec85597505b0f745f8607c4ca9d540e7599 100644 (file)
@@ -36,7 +36,8 @@ import { get, save } from '../../helpers/storage';
 import { isDefined } from '../../helpers/types';
 import { Facet, RawFacet } from '../../types/issues';
 import { SecurityStandard, StandardType } from '../../types/security';
-import { Dict, Issue, Paging, RawQuery, UserBase } from '../../types/types';
+import { Dict, Issue, Paging, RawQuery } from '../../types/types';
+import { UserBase } from '../../types/users';
 
 export interface Query {
   assigned: boolean;
index 75a755a2975cb47f3d6e0e5785b4514f5fa54a8b..fa57e1b9d0f3936e501092c0e64deb94709a79bb 100644 (file)
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import DismissableAlert from '../../../components/ui/DismissableAlert';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn } from '../../../helpers/users';
 import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
 import { ComponentQualifier } from '../../../types/component';
-import { Component, CurrentUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import { PULL_REQUEST_DECORATION_BINDING_CATEGORY } from '../../settings/components/AdditionalCategoryKeys';
 
 export interface FirstAnalysisNextStepsNotifProps {
@@ -130,4 +130,4 @@ export function FirstAnalysisNextStepsNotif(props: FirstAnalysisNextStepsNotifPr
   );
 }
 
-export default withCurrentUser(FirstAnalysisNextStepsNotif);
+export default withCurrentUserContext(FirstAnalysisNextStepsNotif);
index 0ceef54903ae03ccb88fb9e2062a45bb6959d442..2b0909eaafe30a63f8df76101a946d2ad62c5fc2 100644 (file)
@@ -2,7 +2,7 @@
 
 exports[`should render correctly: default 1`] = `
 <Fragment>
-  <Connect(withCurrentUser(FirstAnalysisNextStepsNotif))
+  <withCurrentUserContext(FirstAnalysisNextStepsNotif)
     component={
       Object {
         "breadcrumbs": Array [],
@@ -173,7 +173,7 @@ exports[`should render correctly: default 1`] = `
 
 exports[`should render correctly: empty project 1`] = `
 <Fragment>
-  <Connect(withCurrentUser(FirstAnalysisNextStepsNotif))
+  <withCurrentUserContext(FirstAnalysisNextStepsNotif)
     component={
       Object {
         "breadcrumbs": Array [],
@@ -265,7 +265,7 @@ exports[`should render correctly: empty project 1`] = `
 
 exports[`should render correctly: loading 1`] = `
 <Fragment>
-  <Connect(withCurrentUser(FirstAnalysisNextStepsNotif))
+  <withCurrentUserContext(FirstAnalysisNextStepsNotif)
     component={
       Object {
         "breadcrumbs": Array [],
index f274625ecca4ac49133d494c8573b9d9b886601a..d274642205ad4f21e3cb0728dd503a44b5cf9861 100644 (file)
@@ -13,7 +13,7 @@ exports[`should render correctly for applications 1`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -735,7 +735,7 @@ exports[`should render correctly for applications 2`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -1667,7 +1667,7 @@ exports[`should render correctly for projects 1`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -2389,7 +2389,7 @@ exports[`should render correctly for projects 2`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -3321,7 +3321,7 @@ exports[`should render correctly if branch is misconfigured: hide settings 1`] =
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -3462,7 +3462,7 @@ exports[`should render correctly if branch is misconfigured: show settings 1`] =
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -3609,7 +3609,7 @@ exports[`should render correctly if the data is still loading 1`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -3665,7 +3665,7 @@ exports[`should render correctly if there is no coverage 1`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
@@ -4119,7 +4119,7 @@ exports[`should render correctly if there is no new code measures 1`] = `
     >
       overview.measures
     </h2>
-    <Connect(withCurrentUser(withAppStateContext(ComponentReportActions)))
+    <withCurrentUserContext(withAppStateContext(ComponentReportActions))
       branch={
         Object {
           "analysisDate": "2018-01-01",
index 4c5d74724c9be0ce7f66eedaa009cca63fc61d01..bc040b08461c102e3043cc68558e30eeafa411b4 100644 (file)
@@ -182,7 +182,7 @@ exports[`should render correctly for applications 1`] = `
       />
     </div>
   </div>
-  <Connect(withCurrentUser(SonarLintPromotion))
+  <withCurrentUserContext(SonarLintPromotion)
     qgConditions={
       Array [
         Object {
@@ -395,7 +395,7 @@ exports[`should render correctly for applications 2`] = `
       />
     </div>
   </div>
-  <Connect(withCurrentUser(SonarLintPromotion))
+  <withCurrentUserContext(SonarLintPromotion)
     qgConditions={
       Array [
         Object {
@@ -528,7 +528,7 @@ exports[`should render correctly for projects 1`] = `
       />
     </div>
   </div>
-  <Connect(withCurrentUser(SonarLintPromotion))
+  <withCurrentUserContext(SonarLintPromotion)
     qgConditions={
       Array [
         Object {
@@ -599,7 +599,7 @@ exports[`should render correctly for projects 2`] = `
       </span>
     </div>
   </div>
-  <Connect(withCurrentUser(SonarLintPromotion))
+  <withCurrentUserContext(SonarLintPromotion)
     qgConditions={Array []}
   />
 </div>
@@ -721,7 +721,7 @@ exports[`should render correctly for projects 3`] = `
       />
     </div>
   </div>
-  <Connect(withCurrentUser(SonarLintPromotion))
+  <withCurrentUserContext(SonarLintPromotion)
     qgConditions={
       Array [
         Object {
index fcf72dd063ee271b7139565c593306b124e8ced4..d85ec9885b1b75dc3a2f1d1bdc39a7204baed1a0 100644 (file)
  */
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import TutorialSelection from '../../../components/tutorials/TutorialSelection';
 import { Alert } from '../../../components/ui/Alert';
 import { getBranchLikeDisplayName, isBranch, isMainBranch } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn } from '../../../helpers/users';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
 import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
 import { BranchLike } from '../../../types/branch-like';
 import { ComponentQualifier } from '../../../types/component';
-import { Component, CurrentUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 
 export interface EmptyOverviewProps {
   branchLike?: BranchLike;
@@ -106,8 +105,4 @@ export function EmptyOverview(props: EmptyOverviewProps) {
   );
 }
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(EmptyOverview);
+export default withCurrentUserContext(EmptyOverview);
index f40fafe645b0706a29ff942ad429b5a8f5a8e1ce..e183f17cc6e1eb1944457e0c6e185cb297f08957 100644 (file)
  */
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import SonarLintIcon from '../../../components/icons/SonarLintIcon';
 import { translate } from '../../../helpers/l10n';
 import { MetricKey } from '../../../types/metrics';
 import { QualityGateStatusCondition } from '../../../types/quality-gates';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser } from '../../../types/users';
 
 export interface SonarLintPromotionProps {
   currentUser: CurrentUser;
@@ -77,4 +77,4 @@ export function SonarLintPromotion({ currentUser, qgConditions }: SonarLintPromo
   );
 }
 
-export default withCurrentUser(SonarLintPromotion);
+export default withCurrentUserContext(SonarLintPromotion);
index c3348c42010b8ae00cd0a312c3d6279f4ef2178d..4b272aefe8c3f9d1b2dfbc4a06c8f7e1caae90a9 100644 (file)
@@ -53,7 +53,7 @@ exports[`should render correctly for a failed QG 1`] = `
           }
           level="ERROR"
         />
-        <Connect(withCurrentUser(SonarLintPromotion))
+        <withCurrentUserContext(SonarLintPromotion)
           qgConditions={
             Array [
               Object {
@@ -1424,7 +1424,7 @@ exports[`should render correctly for a passed QG 1`] = `
           }
           level="OK"
         />
-        <Connect(withCurrentUser(SonarLintPromotion))
+        <withCurrentUserContext(SonarLintPromotion)
           qgConditions={Array []}
         />
       </div>
diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts b/server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts
deleted file mode 100644 (file)
index ff02df0..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { connect } from 'react-redux';
-import { getCurrentUser, Store } from '../../../../store/rootReducer';
-import App from './App';
-
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(mapStateToProps)(App);
index 1f5d65fea2dde3675407d53404616ceb49e4fd94..98674c85e9f4a5ec30d8538be547879cad182de3 100644 (file)
@@ -27,6 +27,6 @@ export const globalPermissionsRoutes = [
 
 export const projectPermissionsRoutes = [
   {
-    indexRoute: { component: lazyLoadComponent(() => import('./project/components/AppContainer')) }
+    indexRoute: { component: lazyLoadComponent(() => import('./project/components/App')) }
   }
 ];
index ed14cc84902e043afa6fa5424bf9b7ce7b8d2683..202a721c91627810a0c106a489b1d7b5f3f37972 100644 (file)
@@ -22,6 +22,7 @@ import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
 import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
 import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import ListFooter from '../../../components/controls/ListFooter';
@@ -32,10 +33,10 @@ import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthent
 import { translate } from '../../../helpers/l10n';
 import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages';
 import { get, save } from '../../../helpers/storage';
-import { isLoggedIn } from '../../../helpers/users';
 import { AppState } from '../../../types/appstate';
 import { ComponentQualifier } from '../../../types/component';
-import { CurrentUser, RawQuery } from '../../../types/types';
+import { RawQuery } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import { hasFilterParams, hasViewParams, parseUrlQuery, Query } from '../query';
 import '../styles.css';
 import { Facets, Project } from '../types';
@@ -317,4 +318,4 @@ export class AllProjects extends React.PureComponent<Props, State> {
   }
 }
 
-export default withRouter(withAppStateContext(AllProjects));
+export default withRouter(withCurrentUserContext(withAppStateContext(AllProjects)));
index c1d5e5491caa69e7af321f4ef972d765667b9b8c..01d581d97a7c2e1e20ef1ff807f5e735430dd204 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { connect } from 'react-redux';
 import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
 
-const stateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state)
-});
-
-export default connect(stateToProps)(lazyLoadComponent(() => import('./AllProjects')));
+export default lazyLoadComponent(() => import('./AllProjects'));
index dac6645952cad6f1bad8e0a574fac7a92b33ecfe..f58be34596ec26efbf17ebfb86f48bcbdd8e0bac 100644 (file)
@@ -20,9 +20,9 @@
 import * as React from 'react';
 import { getComponentNavigation } from '../../../api/nav';
 import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import CreateApplicationForm from '../../../app/components/extensions/CreateApplicationForm';
 import { Button } from '../../../components/controls/buttons';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import { Router, withRouter } from '../../../components/hoc/withRouter';
 import { translate } from '../../../helpers/l10n';
 import { getComponentAdminUrl, getComponentOverviewUrl } from '../../../helpers/urls';
@@ -30,7 +30,7 @@ import { hasGlobalPermission } from '../../../helpers/users';
 import { AppState } from '../../../types/appstate';
 import { ComponentQualifier } from '../../../types/component';
 import { Permissions } from '../../../types/permissions';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 
 export interface ApplicationCreationProps {
   appState: AppState;
@@ -85,4 +85,4 @@ export function ApplicationCreation(props: ApplicationCreationProps) {
   );
 }
 
-export default withCurrentUser(withRouter(withAppStateContext(ApplicationCreation)));
+export default withCurrentUserContext(withRouter(withAppStateContext(ApplicationCreation)));
index dedb51d3b8134edea0da63ce16a3d34cc4c501a5..d8886bd7be4abe11c934809dc324198ac69dd528 100644 (file)
  */
 import * as React from 'react';
 import { searchProjects } from '../../../api/components';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import { get } from '../../../helpers/storage';
-import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
-import { CurrentUser } from '../../../types/types';
+import { hasGlobalPermission } from '../../../helpers/users';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import { PROJECTS_ALL, PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE } from '../utils';
 import AllProjectsContainer from './AllProjectsContainer';
 
@@ -98,4 +98,4 @@ export class DefaultPageSelector extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(withRouter(DefaultPageSelector));
+export default withCurrentUserContext(withRouter(DefaultPageSelector));
index af4d77863d46a4d721ceed469c01cd194cfa399c..6a201c2eb1b4959356c96768ab141d2d1ff27ca5 100644 (file)
@@ -22,9 +22,9 @@ import { WithRouterProps } from 'react-router';
 import { Button } from '../../../components/controls/buttons';
 import { withRouter } from '../../../components/hoc/withRouter';
 import { translate } from '../../../helpers/l10n';
-import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { hasGlobalPermission } from '../../../helpers/users';
 import { Permissions } from '../../../types/permissions';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 
 export interface EmptyInstanceProps {
   currentUser: CurrentUser;
index 7faf236ac6f498e7bfc89a5cd5f30c11c73801cd..2b5374afe6164d02b7ef8fa6b415dccaeb8a19b6 100644 (file)
  */
 import * as React from 'react';
 import { IndexLink, Link } from 'react-router';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { translate } from '../../../helpers/l10n';
 import { save } from '../../../helpers/storage';
-import { isLoggedIn } from '../../../helpers/users';
-import { CurrentUser, RawQuery } from '../../../types/types';
+import { RawQuery } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import { PROJECTS_ALL, PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE } from '../utils';
 
 interface Props {
@@ -30,7 +31,7 @@ interface Props {
   query?: RawQuery;
 }
 
-export default class FavoriteFilter extends React.PureComponent<Props> {
+export class FavoriteFilter extends React.PureComponent<Props> {
   handleSaveFavorite = () => {
     save(PROJECTS_DEFAULT_FILTER, PROJECTS_FAVORITE);
   };
@@ -71,3 +72,5 @@ export default class FavoriteFilter extends React.PureComponent<Props> {
     );
   }
 }
+
+export default withCurrentUserContext(FavoriteFilter);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx
deleted file mode 100644 (file)
index 79a2fc6..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { connect } from 'react-redux';
-import { getCurrentUser, Store } from '../../../store/rootReducer';
-import FavoriteFilter from './FavoriteFilter';
-
-function mapStateToProps(state: Store) {
-  return { currentUser: getCurrentUser(state) };
-}
-
-export default connect(mapStateToProps)(FavoriteFilter);
index 594cea686cbb57be50833e84cc92e278aac82540..1fc7c467d409e7e5c5fa4d0839134907babdebbe 100644 (file)
@@ -21,8 +21,8 @@ import classNames from 'classnames';
 import * as React from 'react';
 import HomePageSelect from '../../../components/controls/HomePageSelect';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn } from '../../../helpers/users';
-import { CurrentUser, RawQuery } from '../../../types/types';
+import { RawQuery } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import SearchFilterContainer from '../filters/SearchFilterContainer';
 import ApplicationCreation from './ApplicationCreation';
 import PerspectiveSelect from './PerspectiveSelect';
index 0c4876356819f51d89a58f97f82609da57e2f15e..56df9dbde617daa183674dfdba22a1a3679590fa 100644 (file)
@@ -41,7 +41,7 @@ import TagsFilter from '../filters/TagsFilter';
 import { hasFilterParams } from '../query';
 import { Facets } from '../types';
 import ClearAll from './ClearAll';
-import FavoriteFilterContainer from './FavoriteFilterContainer';
+import FavoriteFilter from './FavoriteFilter';
 
 export interface PageSidebarProps {
   applicationsEnabled: boolean;
@@ -66,7 +66,7 @@ export default function PageSidebar(props: PageSidebarProps) {
 
   return (
     <div>
-      <FavoriteFilterContainer query={linkQuery} />
+      <FavoriteFilter query={linkQuery} />
 
       <div className="projects-facets-header clearfix">
         {isFiltered && <ClearAll onClearAll={props.onClearAll} />}
index aec4f5d6accdef889874399b8403819f6c04eb18..b1cd167c7f5b4b758d8ff2390ffc7f5fb3fa35fb 100644 (file)
@@ -20,9 +20,9 @@
 import * as React from 'react';
 import { Link } from 'react-router';
 import { getAlmSettings } from '../../../api/alm-settings';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { Button } from '../../../components/controls/buttons';
 import Dropdown from '../../../components/controls/Dropdown';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import DropdownIcon from '../../../components/icons/DropdownIcon';
 import EllipsisIcon from '../../../components/icons/EllipsisIcon';
 import { IMPORT_COMPATIBLE_ALMS, IMPORT_COMPATIBLE_ALM_COUNT } from '../../../helpers/constants';
@@ -30,7 +30,7 @@ import { translate } from '../../../helpers/l10n';
 import { hasGlobalPermission } from '../../../helpers/users';
 import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
 import { Permissions } from '../../../types/permissions';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import ProjectCreationMenuItem from './ProjectCreationMenuItem';
 
 interface Props {
@@ -143,4 +143,4 @@ export class ProjectCreationMenu extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(ProjectCreationMenu);
+export default withCurrentUserContext(ProjectCreationMenu);
index 82b3be45bc09972a45e18cf67fde3becdf8b8bb6..38c646cc9eb9c78c15320206b2a0c0b1da7c238f 100644 (file)
@@ -23,7 +23,7 @@ import { List, ListRowProps } from 'react-virtualized/dist/commonjs/List';
 import { WindowScroller } from 'react-virtualized/dist/commonjs/WindowScroller';
 import EmptySearch from '../../../components/common/EmptySearch';
 import { translate } from '../../../helpers/l10n';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser } from '../../../types/users';
 import { Query } from '../query';
 import { Project } from '../types';
 import EmptyFavoriteSearch from './EmptyFavoriteSearch';
index a37123a9c405b4f8079b34bc2f49c66f154fca8e..2dacccf17b2cb768209175b89556096b4d4d1574 100644 (file)
@@ -29,7 +29,7 @@ import {
 } from '../../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
 import { hasGlobalPermission } from '../../../../helpers/users';
-import { CurrentUser } from '../../../../types/types';
+import { CurrentUser } from '../../../../types/users';
 import { DefaultPageSelector } from '../DefaultPageSelector';
 
 jest.mock(
index 12fb0289be656351ed7e281f46ff1848e4ba0983..58da88c51a5865d5ee206ec83cb5acf989676987 100644 (file)
@@ -21,7 +21,7 @@ import { shallow } from 'enzyme';
 import * as React from 'react';
 import { save } from '../../../../helpers/storage';
 import { click } from '../../../../helpers/testUtils';
-import FavoriteFilter from '../FavoriteFilter';
+import { FavoriteFilter } from '../FavoriteFilter';
 
 jest.mock('../../../../helpers/storage', () => ({
   save: jest.fn()
index 8dff4f4dd701dac29584850ce95903701ec0c8db..7d4af9871aaf0ff76cdb58fa8799b685fd41eba5 100644 (file)
@@ -18,13 +18,13 @@ exports[`should render correctly 1`] = `
     <div
       className="display-flex-center"
     >
-      <Connect(withCurrentUser(ProjectCreationMenu))
+      <withCurrentUserContext(ProjectCreationMenu)
         className="little-spacer-right"
       />
-      <Connect(withCurrentUser(withRouter(withAppStateContext(ApplicationCreation))))
+      <withCurrentUserContext(withRouter(withAppStateContext(ApplicationCreation)))
         className="little-spacer-right"
       />
-      <Connect(HomePageSelect)
+      <withCurrentUserContext(HomePageSelect)
         className="spacer-left little-spacer-right"
         currentPage={
           Object {
@@ -94,13 +94,13 @@ exports[`should render correctly while loading 1`] = `
     <div
       className="display-flex-center"
     >
-      <Connect(withCurrentUser(ProjectCreationMenu))
+      <withCurrentUserContext(ProjectCreationMenu)
         className="little-spacer-right"
       />
-      <Connect(withCurrentUser(withRouter(withAppStateContext(ApplicationCreation))))
+      <withCurrentUserContext(withRouter(withAppStateContext(ApplicationCreation)))
         className="little-spacer-right"
       />
-      <Connect(HomePageSelect)
+      <withCurrentUserContext(HomePageSelect)
         className="spacer-left little-spacer-right"
         currentPage={
           Object {
index ed70b96f32fbc4465a656a4e8eb36ce2d21c563c..f1b995718ab859bc8ce326cb746c82007e35c3cc 100644 (file)
@@ -2,7 +2,7 @@
 
 exports[`should render \`leak\` view correctly 1`] = `
 <div>
-  <Connect(FavoriteFilter)
+  <withCurrentUserContext(FavoriteFilter)
     query={
       Object {
         "view": "leak",
@@ -68,7 +68,7 @@ exports[`should render \`leak\` view correctly 1`] = `
 
 exports[`should render \`leak\` view correctly with no applications 1`] = `
 <div>
-  <Connect(FavoriteFilter)
+  <withCurrentUserContext(FavoriteFilter)
     query={
       Object {
         "view": "leak",
@@ -131,7 +131,7 @@ exports[`should render \`leak\` view correctly with no applications 1`] = `
 
 exports[`should render correctly 1`] = `
 <div>
-  <Connect(FavoriteFilter) />
+  <withCurrentUserContext(FavoriteFilter) />
   <div
     className="projects-facets-header clearfix"
   >
@@ -193,7 +193,7 @@ exports[`should render correctly 1`] = `
 
 exports[`should render correctly with no applications 1`] = `
 <div>
-  <Connect(FavoriteFilter) />
+  <withCurrentUserContext(FavoriteFilter) />
   <div
     className="projects-facets-header clearfix"
   >
index d8e0dc3f0ddd9308ef9909faf0d45f6f2130b95c..473850bda5f15e4e27444ad0f8418addb1a52150 100644 (file)
@@ -32,10 +32,9 @@ import TagsList from '../../../../components/tags/TagsList';
 import SizeRating from '../../../../components/ui/SizeRating';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
 import { getProjectUrl } from '../../../../helpers/urls';
-import { isLoggedIn } from '../../../../helpers/users';
 import { ComponentQualifier } from '../../../../types/component';
 import { MetricKey } from '../../../../types/metrics';
-import { CurrentUser } from '../../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../../types/users';
 import { Project } from '../../types';
 import './ProjectCard.css';
 import ProjectCardLanguages from './ProjectCardLanguages';
index f98763cc16ba4fc513c8aededdd3bc4007439070..11e5941ac6479c65382f2b2b709f05f138a9437c 100644 (file)
@@ -23,7 +23,7 @@ import PrivacyBadgeContainer from '../../../../../components/common/PrivacyBadge
 import TagsList from '../../../../../components/tags/TagsList';
 import { mockCurrentUser, mockLoggedInUser } from '../../../../../helpers/testMocks';
 import { ComponentQualifier } from '../../../../../types/component';
-import { CurrentUser } from '../../../../../types/types';
+import { CurrentUser } from '../../../../../types/users';
 import { Project } from '../../../types';
 import ProjectCard from '../ProjectCard';
 import ProjectCardQualityGate from '../ProjectCardQualityGate';
index be9c7f036206705eadd1713fc18ff857cb059be1..8c4c13d024f814ad04319c5b42ff6e1cd4150d39 100644 (file)
 import { debounce, uniq, without } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
 import { getComponents, Project } from '../../api/components';
 import { changeProjectDefaultVisibility } from '../../api/permissions';
 import { getValues } from '../../api/settings';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import Suggestions from '../../app/components/embed-docs-modal/Suggestions';
 import ListFooter from '../../components/controls/ListFooter';
 import { toShortNotSoISOString } from '../../helpers/dates';
 import { translate } from '../../helpers/l10n';
 import { hasGlobalPermission } from '../../helpers/users';
-import { getCurrentUser, Store } from '../../store/rootReducer';
 import { Permissions } from '../../types/permissions';
 import { SettingsKey } from '../../types/settings';
-import { LoggedInUser, Visibility } from '../../types/types';
+import { Visibility } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import CreateProjectForm from './CreateProjectForm';
 import Header from './Header';
 import Projects from './Projects';
@@ -259,8 +259,4 @@ export class App extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: Store) => ({
-  currentUser: getCurrentUser(state) as LoggedInUser
-});
-
-export default connect(mapStateToProps)(App);
+export default withCurrentUserContext(App);
index a3a3cc20122e5c58f4b51e4097d21c50e769bacd..7f96d1b14ff60a26c0d6306e48e3b3c0bf43bcdb 100644 (file)
@@ -26,7 +26,7 @@ import Tooltip from '../../components/controls/Tooltip';
 import QualifierIcon from '../../components/icons/QualifierIcon';
 import DateFormatter from '../../components/intl/DateFormatter';
 import { getComponentOverviewUrl } from '../../helpers/urls';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import './ProjectRow.css';
 import ProjectRowActions from './ProjectRowActions';
 
index f454e0cb80da33cdde28301736cc5b37ef4986ab..6a592edf6d931cc0fdca58f4f112df1e752cb27f 100644 (file)
@@ -24,7 +24,7 @@ import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/
 import DeferredSpinner from '../../components/ui/DeferredSpinner';
 import { translate } from '../../helpers/l10n';
 import { getComponentPermissionsUrl } from '../../helpers/urls';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import ApplyTemplate from '../permissions/project/components/ApplyTemplate';
 import RestoreAccessModal from './RestoreAccessModal';
 
index c4bd4661a4f951599dd0926c689a946fbb791905..577ae936ddd8553771e872071237f7af2f959675 100644 (file)
@@ -21,7 +21,7 @@ import classNames from 'classnames';
 import * as React from 'react';
 import { Project } from '../../api/components';
 import { translate } from '../../helpers/l10n';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import ProjectRow from './ProjectRow';
 
 interface Props {
index 10c6c8d111c9aec4d58cca68dd65e8d491e27947..7c19085d7b9adb45f430e8b179b8577096c79e94 100644 (file)
@@ -24,7 +24,7 @@ import { grantPermissionToUser } from '../../api/permissions';
 import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import Modal from '../../components/controls/Modal';
 import { translate } from '../../helpers/l10n';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 
 interface Props {
   currentUser: Pick<LoggedInUser, 'login'>;
index 1cf8c3504ca23d66e59ca5f937e16d6045cf38a1..3acb730163f1404a4aa6f402b528e2fa66293166 100644 (file)
@@ -22,7 +22,7 @@ import { DeleteButton } from '../../../components/controls/buttons';
 import GroupIcon from '../../../components/icons/GroupIcon';
 import Avatar from '../../../components/ui/Avatar';
 import { Group, isUser } from '../../../types/quality-gates';
-import { UserBase } from '../../../types/types';
+import { UserBase } from '../../../types/users';
 
 export interface PermissionItemProps {
   onClickDelete: (item: UserBase | Group) => void;
index ea81a999981196bed9e51ee525fe2963edd02917..3a9c7e0cc1bae0a12e155775c430bb2c0e284c93 100644 (file)
@@ -28,7 +28,8 @@ import {
   searchUsers
 } from '../../../api/quality-gates';
 import { Group, isUser, SearchPermissionsParameters } from '../../../types/quality-gates';
-import { QualityGate, UserBase } from '../../../types/types';
+import { QualityGate } from '../../../types/types';
+import { UserBase } from '../../../types/users';
 import QualityGatePermissionsRenderer from './QualityGatePermissionsRenderer';
 
 interface Props {
index de564c13adb8c4988e88fffccf72472c240ae424..81bb2ec4ad00f83b5cdbeb952923ef137f588297 100644 (file)
@@ -21,7 +21,8 @@ import { debounce } from 'lodash';
 import * as React from 'react';
 import { searchGroups, searchUsers } from '../../../api/quality-gates';
 import { Group, SearchPermissionsParameters } from '../../../types/quality-gates';
-import { QualityGate, UserBase } from '../../../types/types';
+import { QualityGate } from '../../../types/types';
+import { UserBase } from '../../../types/users';
 import QualityGatePermissionsAddModalRenderer from './QualityGatePermissionsAddModalRenderer';
 
 interface Props {
index 441f47666fd720a79782105e1db2c613b1dd9535..ea2f2fbd70c10128824cd4fddea9628b81ae9d3d 100644 (file)
@@ -25,7 +25,7 @@ import GroupIcon from '../../../components/icons/GroupIcon';
 import Avatar from '../../../components/ui/Avatar';
 import { translate } from '../../../helpers/l10n';
 import { Group, isUser } from '../../../types/quality-gates';
-import { UserBase } from '../../../types/types';
+import { UserBase } from '../../../types/users';
 
 export interface QualityGatePermissionsAddModalRendererProps {
   onClose: () => void;
index f061811ccb0d1e685dbfadc09d0b7ca75ba3bbe5..708c24269de47fc28500f3efbaac4798680a343a 100644 (file)
@@ -24,7 +24,8 @@ import ConfirmModal from '../../../components/controls/ConfirmModal';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate } from '../../../helpers/l10n';
 import { Group, isUser } from '../../../types/quality-gates';
-import { QualityGate, UserBase } from '../../../types/types';
+import { QualityGate } from '../../../types/types';
+import { UserBase } from '../../../types/users';
 import PermissionItem from './PermissionItem';
 import QualityGatePermissionsAddModal from './QualityGatePermissionsAddModal';
 
index 66c0f94b6a7e81cd476aacb62e97a0f8bfa406af..50fa7a08089c521ff5cff4d7a11c8cae297ab1e1 100644 (file)
@@ -24,14 +24,13 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import { getMeasures } from '../../api/measures';
 import { getSecurityHotspotList, getSecurityHotspots } from '../../api/security-hotspots';
-import { withCurrentUser } from '../../components/hoc/withCurrentUser';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import { Router } from '../../components/hoc/withRouter';
 import { getLeakValue } from '../../components/measure/utils';
 import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../helpers/branch-like';
 import { KeyboardCodes, KeyboardKeys } from '../../helpers/keycodes';
 import { scrollToElement } from '../../helpers/scrolling';
 import { getStandards } from '../../helpers/security-standard';
-import { isLoggedIn } from '../../helpers/users';
 import { fetchBranchStatus } from '../../store/rootActions';
 import { BranchLike } from '../../types/branch-like';
 import { SecurityStandard, Standards } from '../../types/security';
@@ -42,7 +41,8 @@ import {
   HotspotStatusFilter,
   RawHotspot
 } from '../../types/security-hotspots';
-import { Component, CurrentUser, Dict } from '../../types/types';
+import { Component, Dict } from '../../types/types';
+import { CurrentUser, isLoggedIn } from '../../types/users';
 import SecurityHotspotsAppRenderer from './SecurityHotspotsAppRenderer';
 import './styles.css';
 import { getLocations, SECURITY_STANDARDS } from './utils';
@@ -547,4 +547,4 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps = { fetchBranchStatus };
 
-export default withCurrentUser(connect(null, mapDispatchToProps)(SecurityHotspotsApp));
+export default withCurrentUserContext(connect(null, mapDispatchToProps)(SecurityHotspotsApp));
index bd4730c41102de73873308adbbbedeb1684d393f..fa7b43b13fa117a479b6147821606aa132a625b2 100644 (file)
@@ -16,7 +16,7 @@ exports[`should render correctly 1`] = `
   <A11ySkipTarget
     anchor="security_hotspots_main"
   />
-  <Connect(withCurrentUser(FilterBar))
+  <withCurrentUserContext(FilterBar)
     component={
       Object {
         "breadcrumbs": Array [],
@@ -323,7 +323,7 @@ exports[`should render correctly with hotspots 1`] = `
   <A11ySkipTarget
     anchor="security_hotspots_main"
   />
-  <Connect(withCurrentUser(FilterBar))
+  <withCurrentUserContext(FilterBar)
     component={
       Object {
         "breadcrumbs": Array [],
@@ -461,7 +461,7 @@ exports[`should render correctly: loading 1`] = `
   <A11ySkipTarget
     anchor="security_hotspots_main"
   />
-  <Connect(withCurrentUser(FilterBar))
+  <withCurrentUserContext(FilterBar)
     component={
       Object {
         "breadcrumbs": Array [],
@@ -527,7 +527,7 @@ exports[`should render correctly: no hotspots with filters 1`] = `
   <A11ySkipTarget
     anchor="security_hotspots_main"
   />
-  <Connect(withCurrentUser(FilterBar))
+  <withCurrentUserContext(FilterBar)
     component={
       Object {
         "breadcrumbs": Array [],
index 720b7fd545497789c957c2a6f8fcee5848959335..ccbb2979f252d8ac1aeb85981be4916d847a1bf5 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import RadioToggle from '../../../components/controls/RadioToggle';
 import SelectLegacy from '../../../components/controls/SelectLegacy';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import Measure from '../../../components/measure/Measure';
 import CoverageRating from '../../../components/ui/CoverageRating';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn } from '../../../helpers/users';
 import { ComponentQualifier } from '../../../types/component';
 import { HotspotFilters, HotspotStatusFilter } from '../../../types/security-hotspots';
-import { Component, CurrentUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 
 export interface FilterBarProps {
   currentUser: CurrentUser;
@@ -169,4 +169,4 @@ export function FilterBar(props: FilterBarProps) {
   );
 }
 
-export default withCurrentUser(FilterBar);
+export default withCurrentUserContext(FilterBar);
index 6ae043aa9ca71cd0838a08920f09ae90d59c27d2..1720cd7d77e315dc8c1e9edd14769e22b8f90eaa 100644 (file)
  */
 import classNames from 'classnames';
 import * as React from 'react';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { ButtonLink } from '../../../components/controls/buttons';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn } from '../../../helpers/users';
 import { Hotspot } from '../../../types/security-hotspots';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import './HotspotPrimaryLocationBox.css';
 
 export interface HotspotPrimaryLocationBoxProps {
@@ -67,4 +66,4 @@ export function HotspotPrimaryLocationBox(props: HotspotPrimaryLocationBoxProps)
   );
 }
 
-export default withCurrentUser(HotspotPrimaryLocationBox);
+export default withCurrentUserContext(HotspotPrimaryLocationBox);
index 38454514e9c1aa2610fd158ab024c2c414f16a88..68813b61263c3d466cbcebdb46137fd8a38d3ea9 100644 (file)
@@ -26,9 +26,8 @@ import {
 import FormattingTips from '../../../components/common/FormattingTips';
 import { Button } from '../../../components/controls/buttons';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn } from '../../../helpers/users';
 import { Hotspot } from '../../../types/security-hotspots';
-import { CurrentUser } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import HotspotReviewHistory from './HotspotReviewHistory';
 
 interface Props {
index a79ed7659fc49f08febdb015cba824024f2d8a33..7a00dfede1e0f251ef5cd9b2ae0cff999d54eae5 100644 (file)
 
 import styled from '@emotion/styled';
 import React from 'react';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { colors, sizes } from '../../../app/theme';
 import { ClipboardButton, ClipboardIconButton } from '../../../components/controls/clipboard';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import LinkIcon from '../../../components/icons/LinkIcon';
 import QualifierIcon from '../../../components/icons/QualifierIcon';
 import { getBranchLikeQuery } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
 import { collapsedDirFromPath, fileFromPath } from '../../../helpers/path';
 import { getComponentSecurityHotspotsUrl, getPathUrlAsString } from '../../../helpers/urls';
-import { isLoggedIn } from '../../../helpers/users';
 import { BranchLike } from '../../../types/branch-like';
 import { ComponentQualifier } from '../../../types/component';
 import { Hotspot } from '../../../types/security-hotspots';
-import { Component, CurrentUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 import HotspotOpenInIdeButton from './HotspotOpenInIdeButton';
 
 export interface HotspotSnippetHeaderProps {
@@ -113,4 +113,4 @@ const FilePath = styled.div`
   }
 `;
 
-export default withCurrentUser(HotspotSnippetHeader);
+export default withCurrentUserContext(HotspotSnippetHeader);
index 0fc2f8540b05468135e5990cd1e0efeec89f7a27..81ee7616719a99a6ff43e86336578668cd0379e0 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { fillBranchLike } from '../../../helpers/branch-like';
 import { Hotspot, HotspotStatusOption } from '../../../types/security-hotspots';
-import { Component, CurrentUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { CurrentUser } from '../../../types/users';
 import { HotspotHeader } from './HotspotHeader';
 import HotspotReviewHistoryAndComments from './HotspotReviewHistoryAndComments';
 import HotspotSnippetContainer from './HotspotSnippetContainer';
@@ -100,4 +101,4 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) {
   );
 }
 
-export default withCurrentUser(HotspotViewerRenderer);
+export default withCurrentUserContext(HotspotViewerRenderer);
index b360e9d574fe8110510cb3626bf60247bc16298e..5f0e61cae10345dcb82fdb126d21c6eff6ae6f47 100644 (file)
@@ -27,7 +27,7 @@ import {
 import { mockHotspot } from '../../../../helpers/mocks/security-hotspots';
 import { mockCurrentUser } from '../../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { isLoggedIn } from '../../../../helpers/users';
+import { isLoggedIn } from '../../../../types/users';
 import HotspotReviewHistory from '../HotspotReviewHistory';
 import HotspotReviewHistoryAndComments from '../HotspotReviewHistoryAndComments';
 
@@ -37,7 +37,7 @@ jest.mock('../../../../api/security-hotspots', () => ({
   editSecurityHotspotComment: jest.fn().mockResolvedValue({})
 }));
 
-jest.mock('../../../../helpers/users', () => ({ isLoggedIn: jest.fn(() => true) }));
+jest.mock('../../../../types/users', () => ({ isLoggedIn: jest.fn(() => true) }));
 
 it('should render correctly', () => {
   const wrapper = shallowRender();
index 2f09502b2840d1c0a92b60ac446ea8865154dcfb..eb76f5685b94e1b2398d6261b1b000704be58da7 100644 (file)
@@ -42,7 +42,7 @@ exports[`should render correctly 1`] = `
   <div
     className="display-flex-space-between"
   >
-    <Connect(withCurrentUser(Status))
+    <withCurrentUserContext(Status)
       hotspot={
         Object {
           "assignee": "assignee",
@@ -142,7 +142,7 @@ exports[`should render correctly 1`] = `
         >
           assignee: 
         </div>
-        <Connect(withCurrentUser(Assignee))
+        <withCurrentUserContext(Assignee)
           hotspot={
             Object {
               "assignee": "assignee",
index 4303a69112893a2910d7d49c84d635090dd2c7f8..65a827e5192e04e8231c40d314de198324b87647 100644 (file)
@@ -7,7 +7,7 @@ exports[`should render correctly 1`] = `
   >
     hotspots.no_associated_lines
   </p>
-  <Connect(withCurrentUser(HotspotSnippetHeader))
+  <withCurrentUserContext(HotspotSnippetHeader)
     branchLike={
       Object {
         "analysisDate": "2018-01-01",
@@ -143,7 +143,7 @@ exports[`should render correctly when secondary location is selected: with selec
   >
     hotspots.no_associated_lines
   </p>
-  <Connect(withCurrentUser(HotspotSnippetHeader))
+  <withCurrentUserContext(HotspotSnippetHeader)
     branchLike={
       Object {
         "analysisDate": "2018-01-01",
@@ -274,7 +274,7 @@ exports[`should render correctly when secondary location is selected: with selec
 
 exports[`should render correctly: with sourcelines 1`] = `
 <Fragment>
-  <Connect(withCurrentUser(HotspotSnippetHeader))
+  <withCurrentUserContext(HotspotSnippetHeader)
     branchLike={
       Object {
         "analysisDate": "2018-01-01",
index 1e5149058ec66a5f00d783a0fae21ce2cc66529e..0e615b87137556d59e0af1f47e6743000d972640 100644 (file)
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-<Connect(withCurrentUser(HotspotViewerRenderer))
+<withCurrentUserContext(HotspotViewerRenderer)
   commentTextRef={
     Object {
       "current": null,
@@ -40,7 +40,7 @@ exports[`should render correctly 1`] = `
 `;
 
 exports[`should render correctly 2`] = `
-<Connect(withCurrentUser(HotspotViewerRenderer))
+<withCurrentUserContext(HotspotViewerRenderer)
   commentTextRef={
     Object {
       "current": null,
index 2d8a0e9fc028b4d0a5c91d035aaa35bb178b718b..89a43531b3bb06dd2f4aa5ce5d73c27066113065 100644 (file)
  */
 import * as React from 'react';
 import { assignSecurityHotspot } from '../../../../api/security-hotspots';
+import withCurrentUserContext from '../../../../app/components/current-user/withCurrentUserContext';
 import addGlobalSuccessMessage from '../../../../app/utils/addGlobalSuccessMessage';
-import { withCurrentUser } from '../../../../components/hoc/withCurrentUser';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
-import { isLoggedIn } from '../../../../helpers/users';
 import { Hotspot, HotspotStatus } from '../../../../types/security-hotspots';
-import { CurrentUser, UserActive } from '../../../../types/types';
+import { CurrentUser, isLoggedIn, UserActive } from '../../../../types/users';
 import AssigneeRenderer from './AssigneeRenderer';
 
 interface Props {
@@ -107,4 +106,4 @@ export class Assignee extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(Assignee);
+export default withCurrentUserContext(Assignee);
index 03a658e5e69f3832a5b3fef4ee65b97225de63e8..f226c2f0912ff199ae8687d3ec08a4ca44e48451 100644 (file)
@@ -23,7 +23,7 @@ import EscKeydownHandler from '../../../../components/controls/EscKeydownHandler
 import OutsideClickHandler from '../../../../components/controls/OutsideClickHandler';
 import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
-import { LoggedInUser, UserActive, UserBase } from '../../../../types/types';
+import { LoggedInUser, UserActive, UserBase } from '../../../../types/users';
 import AssigneeSelection from './AssigneeSelection';
 
 export interface AssigneeRendererProps {
index 6707ca208cf1801b9c87e6e2675d2e8c1b3ce510..70058208c50aac150283e21faae4c3234bffecb1 100644 (file)
@@ -22,8 +22,7 @@ import * as React from 'react';
 import { searchUsers } from '../../../../api/users';
 import { KeyboardCodes } from '../../../../helpers/keycodes';
 import { translate } from '../../../../helpers/l10n';
-import { isUserActive } from '../../../../helpers/users';
-import { LoggedInUser, UserActive } from '../../../../types/types';
+import { isUserActive, LoggedInUser, UserActive } from '../../../../types/users';
 import AssigneeSelectionRenderer from './AssigneeSelectionRenderer';
 
 interface Props {
index 30f2c0838ecfebd58cc1a7384ba4d441b8323545..3610cfdc3767177be353c10a3991182d6e90420c 100644 (file)
@@ -25,7 +25,7 @@ import Avatar from '../../../../components/ui/Avatar';
 import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
 import { PopupPlacement } from '../../../../components/ui/popups';
 import { translate } from '../../../../helpers/l10n';
-import { UserActive } from '../../../../types/types';
+import { UserActive } from '../../../../types/users';
 import './AssigneeSelection.css';
 
 export interface HotspotAssigneeSelectRendererProps {
index 298b12abeff2f8add4ddc6a81988f474628f281b..4bf5b33262e3367d8de92293c9395a549a3d4536 100644 (file)
@@ -25,7 +25,7 @@ import { mockHotspot } from '../../../../../helpers/mocks/security-hotspots';
 import { mockCurrentUser, mockUser } from '../../../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../../../helpers/testUtils';
 import { HotspotStatus } from '../../../../../types/security-hotspots';
-import { UserActive } from '../../../../../types/types';
+import { UserActive } from '../../../../../types/users';
 import { Assignee } from '../Assignee';
 import AssigneeRenderer from '../AssigneeRenderer';
 
index 0fe366d761886afdbee83981794c49b53a999698..32e3de350ee4423e07ed935fcff8e3c4e81e67b9 100644 (file)
@@ -23,7 +23,7 @@ import { EditButton } from '../../../../../components/controls/buttons';
 import OutsideClickHandler from '../../../../../components/controls/OutsideClickHandler';
 import { mockLoggedInUser, mockUser } from '../../../../../helpers/testMocks';
 import { click } from '../../../../../helpers/testUtils';
-import { UserActive } from '../../../../../types/types';
+import { UserActive } from '../../../../../types/users';
 import AssigneeRenderer, { AssigneeRendererProps } from '../AssigneeRenderer';
 import AssigneeSelection from '../AssigneeSelection';
 
index 8835bcea8157bb73eb34473a99275e14a8125691..0e59e23a6e29aebbbb47ae67792c21450e047a3f 100644 (file)
@@ -23,7 +23,7 @@ import { searchUsers } from '../../../../../api/users';
 import { KeyboardCodes } from '../../../../../helpers/keycodes';
 import { mockLoggedInUser, mockUser } from '../../../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../../../helpers/testUtils';
-import { UserActive } from '../../../../../types/types';
+import { UserActive } from '../../../../../types/users';
 import AssigneeSelection from '../AssigneeSelection';
 
 jest.mock('../../../../../api/users', () => ({
index 19bfc3bc967fe6777dee354bf6ac25c115014300..f686f2fc639354ba25b372519cbf9feb07a64943 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { mockUser } from '../../../../../helpers/testMocks';
-import { UserActive } from '../../../../../types/types';
+import { UserActive } from '../../../../../types/users';
 import AssigneeSelectionRenderer, {
   HotspotAssigneeSelectRendererProps
 } from '../AssigneeSelectionRenderer';
index 078e5023db5c1d2078e5e11ef68a985e67e5b9a1..eb075fb4aa6b0e3bd42c0a62ea3dc3cc32583b0a 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import withCurrentUserContext from '../../../../app/components/current-user/withCurrentUserContext';
 import { Button } from '../../../../components/controls/buttons';
 import { DropdownOverlay } from '../../../../components/controls/Dropdown';
 import Toggler from '../../../../components/controls/Toggler';
 import Tooltip from '../../../../components/controls/Tooltip';
-import { withCurrentUser } from '../../../../components/hoc/withCurrentUser';
 import DropdownIcon from '../../../../components/icons/DropdownIcon';
 import { PopupPlacement } from '../../../../components/ui/popups';
 import { translate } from '../../../../helpers/l10n';
-import { isLoggedIn } from '../../../../helpers/users';
 import { Hotspot, HotspotStatusOption } from '../../../../types/security-hotspots';
-import { CurrentUser } from '../../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../../types/users';
 import { getStatusOptionFromStatusAndResolution } from '../../utils';
 import StatusDescription from './StatusDescription';
 import StatusSelection from './StatusSelection';
@@ -87,4 +86,4 @@ export function Status(props: StatusProps) {
   );
 }
 
-export default withCurrentUser(Status);
+export default withCurrentUserContext(Status);
index f65dc0988a2800f6bc99717eaa6998f57441bdd1..2e9bfed908d6cddbcd212482c898e1d43ba8ada1 100644 (file)
  */
 import * as React from 'react';
 import { sendTestEmail } from '../../../api/settings';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { SubmitButton } from '../../../components/controls/buttons';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import { Alert } from '../../../components/ui/Alert';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { parseError } from '../../../helpers/request';
-import { LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 
 interface Props {
   currentUser: LoggedInUser;
@@ -175,4 +175,4 @@ export class EmailForm extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(EmailForm);
+export default withCurrentUserContext(EmailForm);
index 8787399d313611913b3015b788b1cd620d4c1fbc..cb5c37892f548369867e237ce3a0e2dc03d6c94e 100644 (file)
@@ -93,7 +93,7 @@ exports[`should render additional categories component correctly 4`] = `
 `;
 
 exports[`should render additional categories component correctly 5`] = `
-<Connect(withCurrentUser(PRDecorationBinding))
+<withCurrentUserContext(PRDecorationBinding)
   component={
     Object {
       "breadcrumbs": Array [],
index c0449cc8a04cacf8411750db006643b922953617..ff66d64f6a40bcf6b22facce344d41bcc77961db 100644 (file)
@@ -36,7 +36,7 @@ exports[`should render correctly 1`] = `
         ]
       }
     />
-    <Connect(withCurrentUser(EmailForm)) />
+    <withCurrentUserContext(EmailForm) />
   </li>
   <li
     key="qg"
index 885689f50c999f8e7efa8322c8d8ac615ac2d7af..dcd438581573dda5ea7d76eb4e8607903bde574a 100644 (file)
@@ -29,8 +29,8 @@ import {
   setProjectGitlabBinding,
   validateProjectAlmBinding
 } from '../../../../api/alm-settings';
+import withCurrentUserContext from '../../../../app/components/current-user/withCurrentUserContext';
 import throwGlobalError from '../../../../app/utils/throwGlobalError';
-import { withCurrentUser } from '../../../../components/hoc/withCurrentUser';
 import { HttpStatus } from '../../../../helpers/request';
 import { hasGlobalPermission } from '../../../../helpers/users';
 import {
@@ -40,7 +40,8 @@ import {
   ProjectAlmBindingResponse
 } from '../../../../types/alm-settings';
 import { Permissions } from '../../../../types/permissions';
-import { Component, CurrentUser } from '../../../../types/types';
+import { Component } from '../../../../types/types';
+import { CurrentUser } from '../../../../types/users';
 import PRDecorationBindingRenderer from './PRDecorationBindingRenderer';
 
 type FormData = Omit<ProjectAlmBindingResponse, 'alm'>;
@@ -351,4 +352,4 @@ export class PRDecorationBinding extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(PRDecorationBinding);
+export default withCurrentUserContext(PRDecorationBinding);
index 274ea824bba5320d21139aef44d043e3dff895d2..f439306338cfe6f603d2c6201c1d8b0e18b1dd0f 100644 (file)
@@ -16,7 +16,7 @@ exports[`should render correctly: cluster sysinfo 1`] = `
   <div
     className="page-notifs"
   >
-    <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
+    <withCurrentUserContext(withAppStateContext(UpdateNotification))
       dismissable={false}
     />
   </div>
@@ -209,7 +209,7 @@ exports[`should render correctly: loading 1`] = `
   <div
     className="page-notifs"
   >
-    <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
+    <withCurrentUserContext(withAppStateContext(UpdateNotification))
       dismissable={false}
     />
   </div>
@@ -232,7 +232,7 @@ exports[`should render correctly: stand-alone sysinfo 1`] = `
   <div
     className="page-notifs"
   >
-    <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
+    <withCurrentUserContext(withAppStateContext(UpdateNotification))
       dismissable={false}
     />
   </div>
index 8cf1218ab0e2fe8c1ef03fa17916afb7f81b2fd4..bce81dc134d2e8b958372cecc00b5f1db2107114 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import TutorialSelection from '../../../components/tutorials/TutorialSelection';
 import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication';
-import { isLoggedIn } from '../../../helpers/users';
 import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
-import { Component, CurrentUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { CurrentUser, isLoggedIn } from '../../../types/users';
 
 export interface TutorialsAppProps {
   component: Component;
@@ -50,4 +50,4 @@ export function TutorialsApp(props: TutorialsAppProps) {
   );
 }
 
-export default withCurrentUser(TutorialsApp);
+export default withCurrentUserContext(TutorialsApp);
index 932e5e11f5d3356a3ce161e43f9cca7ef623dcfd..a7e2877cec74c994783edf2db53e2ffb198d8a4d 100644 (file)
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { getIdentityProviders, searchUsers } from '../../api/users';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import Suggestions from '../../app/components/embed-docs-modal/Suggestions';
 import ListFooter from '../../components/controls/ListFooter';
-import { withCurrentUser } from '../../components/hoc/withCurrentUser';
 import { Location, Router, withRouter } from '../../components/hoc/withRouter';
 import { translate } from '../../helpers/l10n';
-import { IdentityProvider, Paging, User } from '../../types/types';
+import { IdentityProvider, Paging } from '../../types/types';
+import { User } from '../../types/users';
 import Header from './Header';
 import Search from './Search';
 import UsersList from './UsersList';
@@ -141,4 +142,4 @@ export class UsersApp extends React.PureComponent<Props, State> {
   }
 }
 
-export default withRouter(withCurrentUser(UsersApp));
+export default withRouter(withCurrentUserContext(UsersApp));
index 5de6057fde4c318df55b0409e7821a215db116c7..d4761fc7c609101aea26d1614ce21f250cae9a47 100644 (file)
@@ -19,7 +19,8 @@
  */
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
-import { IdentityProvider, User } from '../../types/types';
+import { IdentityProvider } from '../../types/types';
+import { User } from '../../types/users';
 import UserListItem from './components/UserListItem';
 
 interface Props {
index 34909a28c5a8f7c97995b08b3df3c99efccc65aa..8f4b6414e0f27bd3f6668eb57a84d5f5f913dc1e 100644 (file)
@@ -22,7 +22,7 @@ import { deactivateUser } from '../../../api/users';
 import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import Modal from '../../../components/controls/Modal';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { UserActive } from '../../../types/types';
+import { UserActive } from '../../../types/users';
 
 export interface Props {
   onClose: () => void;
index 82e1a4938b6af49381d4f3d9989b672160def392..0a112561f3acb2810239358d0d392349c551d749 100644 (file)
@@ -27,7 +27,7 @@ import SelectList, {
   SelectListSearchParams
 } from '../../../components/controls/SelectList';
 import { translate } from '../../../helpers/l10n';
-import { User } from '../../../types/types';
+import { User } from '../../../types/users';
 
 interface Props {
   onClose: () => void;
index 8c4a3bbc80641edec428b0e8a8cc97c2d60ae910..b4b0d24b7766b6f61c26ff1fd854735124933d4b 100644 (file)
@@ -28,7 +28,7 @@ import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
 import { translate } from '../../../helpers/l10n';
 import { parseError } from '../../../helpers/request';
-import { User } from '../../../types/types';
+import { User } from '../../../types/users';
 
 interface Props {
   isCurrentUser: boolean;
index 448e54cde47769735035b184317bcc28514e2dba..85981cf315e3eee76f4f5a179f11fdc1fd200dae 100644 (file)
@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
 import { ResetButtonLink } from '../../../components/controls/buttons';
 import Modal from '../../../components/controls/Modal';
 import { translate } from '../../../helpers/l10n';
-import { User } from '../../../types/types';
+import { User } from '../../../types/users';
 import TokensForm from './TokensForm';
 
 interface Props {
index dc7186452988ae88b00cd18b6e1b7fb9b904fbae..32d01fd35c5898c73dc267d8d174da4133c7e6f6 100644 (file)
@@ -23,8 +23,7 @@ import ActionsDropdown, {
   ActionsDropdownItem
 } from '../../../components/controls/ActionsDropdown';
 import { translate } from '../../../helpers/l10n';
-import { isUserActive } from '../../../helpers/users';
-import { User } from '../../../types/types';
+import { isUserActive, User } from '../../../types/users';
 import DeactivateForm from './DeactivateForm';
 import PasswordForm from './PasswordForm';
 import UserForm from './UserForm';
index c61a68f94be0f17286461e4b168dac7754d455c6..e0d954293d63203354bb5f4472577df4cb833e0d 100644 (file)
@@ -28,7 +28,7 @@ import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { parseError } from '../../../helpers/request';
-import { User } from '../../../types/types';
+import { User } from '../../../types/users';
 import UserScmAccountInput from './UserScmAccountInput';
 
 export interface Props {
index c35ef36a9e2a5a136cad13705ac471ab0f50a11e..cea99845583aef66d62d94679ec1e5795b3c34fb 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { ButtonIcon } from '../../../components/controls/buttons';
 import BulletListIcon from '../../../components/icons/BulletListIcon';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { User } from '../../../types/types';
+import { User } from '../../../types/users';
 import GroupsForm from './GroupsForm';
 
 interface Props {
index ad0cc57e7272029a85e95c68a49a70694bd1956e..8dd7b8784f9d2959b16cc7ccac74a707ed304075 100644 (file)
@@ -23,7 +23,8 @@ import BulletListIcon from '../../../components/icons/BulletListIcon';
 import DateFromNow from '../../../components/intl/DateFromNow';
 import Avatar from '../../../components/ui/Avatar';
 import { translate } from '../../../helpers/l10n';
-import { IdentityProvider, User } from '../../../types/types';
+import { IdentityProvider } from '../../../types/types';
+import { User } from '../../../types/users';
 import TokensFormModal from './TokensFormModal';
 import UserActions from './UserActions';
 import UserGroups from './UserGroups';
index 3980cad5176d036ac0420bf0a6083907ac17fbfb..6cb47ae2a3a3b5e61b7fef4dfb4833bfcafa5aa3 100644 (file)
@@ -21,7 +21,8 @@ import * as React from 'react';
 import { colors } from '../../../app/theme';
 import { getTextColor } from '../../../helpers/colors';
 import { getBaseUrl } from '../../../helpers/system';
-import { IdentityProvider, User } from '../../../types/types';
+import { IdentityProvider } from '../../../types/types';
+import { User } from '../../../types/users';
 
 export interface Props {
   identityProvider?: IdentityProvider;
index 1a713c128b2fd6e9f094552b2e4b2c9c8b846465..ab949c308f97457c47a17bdfbb4f022e17decf2e 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { click } from '../../../../helpers/testUtils';
-import { User } from '../../../../types/types';
+import { User } from '../../../../types/users';
 import UserListItem from '../UserListItem';
 
 jest.mock('../../../../components/intl/DateFromNow');
index c058cb7a660173012c37e835c1c8ad0e1a6eaba1..f0a776dcf435d249155e896d21e6d9780fe3dec8 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import { dismissAnalysisWarning, getTask } from '../../api/ce';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import { ButtonLink } from '../../components/controls/buttons';
 import Modal from '../../components/controls/Modal';
 import WarningIcon from '../../components/icons/WarningIcon';
@@ -26,8 +27,7 @@ import DeferredSpinner from '../../components/ui/DeferredSpinner';
 import { translate } from '../../helpers/l10n';
 import { sanitizeStringRestricted } from '../../helpers/sanitize';
 import { TaskWarning } from '../../types/tasks';
-import { CurrentUser } from '../../types/types';
-import { withCurrentUser } from '../hoc/withCurrentUser';
+import { CurrentUser } from '../../types/users';
 
 interface Props {
   componentKey?: string;
@@ -171,4 +171,4 @@ export class AnalysisWarningsModal extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(AnalysisWarningsModal);
+export default withCurrentUserContext(AnalysisWarningsModal);
index 8d845271f11c5353e8da354d900db382cb8bd8b8..a9f7dd0fa7c66dbd88dcd34e93d6da714fc834f9 100644 (file)
@@ -24,7 +24,7 @@ import { Alert } from '../../components/ui/Alert';
 import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
 import { translate } from '../../helpers/l10n';
-import { LoggedInUser } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 
 interface Props {
   className?: string;
index 88c25f5d75d8daf4b9e70cda7cf4aae99653c690..dcb3359e16703408d710660c121d38a35556d48d 100644 (file)
@@ -24,15 +24,15 @@ import {
   unsubscribeFromEmailReport
 } from '../../api/component-report';
 import withAppStateContext from '../../app/components/app-state/withAppStateContext';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import addGlobalSuccessMessage from '../../app/utils/addGlobalSuccessMessage';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { isLoggedIn } from '../../helpers/users';
 import { AppState } from '../../types/appstate';
 import { Branch } from '../../types/branch-like';
 import { ComponentQualifier } from '../../types/component';
 import { ComponentReportStatus } from '../../types/component-report';
-import { Component, CurrentUser } from '../../types/types';
-import { withCurrentUser } from '../hoc/withCurrentUser';
+import { Component } from '../../types/types';
+import { CurrentUser, isLoggedIn } from '../../types/users';
 import ComponentReportActionsRenderer from './ComponentReportActionsRenderer';
 
 interface Props {
@@ -134,4 +134,4 @@ export class ComponentReportActions extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(withAppStateContext(ComponentReportActions));
+export default withCurrentUserContext(withAppStateContext(ComponentReportActions));
index d7be205eb0a9044c82dc9f8922e948a76dbcd211..73e3a217c6d0eb9da3aafdeec6c4c1bfa705a9d7 100644 (file)
  */
 import classNames from 'classnames';
 import * as React from 'react';
-import { connect } from 'react-redux';
+import { setHomePage } from '../../api/users';
+import { CurrentUserContextInterface } from '../../app/components/current-user/CurrentUserContext';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import { ButtonLink } from '../../components/controls/buttons';
 import Tooltip from '../../components/controls/Tooltip';
 import HomeIcon from '../../components/icons/HomeIcon';
 import { translate } from '../../helpers/l10n';
-import { isLoggedIn } from '../../helpers/users';
-import { getCurrentUser, Store } from '../../store/rootReducer';
-import { setHomePage } from '../../store/users';
-import { CurrentUser, HomePage } from '../../types/types';
+import { isSameHomePage } from '../../helpers/users';
+import { HomePage, isLoggedIn } from '../../types/users';
 
-interface StateProps {
-  currentUser: CurrentUser;
-}
-
-interface DispatchProps {
-  setHomePage: (homepage: HomePage) => void;
-}
-
-interface Props extends StateProps, DispatchProps {
+interface Props
+  extends Pick<CurrentUserContextInterface, 'currentUser' | 'updateCurrentUserHomepage'> {
   className?: string;
   currentPage: HomePage;
 }
@@ -45,12 +38,22 @@ interface Props extends StateProps, DispatchProps {
 export const DEFAULT_HOMEPAGE: HomePage = { type: 'PROJECTS' };
 
 export class HomePageSelect extends React.PureComponent<Props> {
+  async setCurrentUserHomepage(homepage: HomePage) {
+    const { currentUser } = this.props;
+
+    if (currentUser && isLoggedIn(currentUser)) {
+      await setHomePage(homepage);
+
+      this.props.updateCurrentUserHomepage(homepage);
+    }
+  }
+
   handleClick = () => {
-    this.props.setHomePage(this.props.currentPage);
+    this.setCurrentUserHomepage(this.props.currentPage);
   };
 
   handleReset = () => {
-    this.props.setHomePage(DEFAULT_HOMEPAGE);
+    this.setCurrentUserHomepage(DEFAULT_HOMEPAGE);
   };
 
   render() {
@@ -88,18 +91,4 @@ export class HomePageSelect extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: Store): StateProps => ({
-  currentUser: getCurrentUser(state)
-});
-
-const mapDispatchToProps: DispatchProps = { setHomePage };
-
-export default connect(mapStateToProps, mapDispatchToProps)(HomePageSelect);
-
-function isSameHomePage(a: HomePage, b: HomePage) {
-  return (
-    a.type === b.type &&
-    (a as any).branch === (b as any).branch &&
-    (a as any).component === (b as any).component
-  );
-}
+export default withCurrentUserContext(HomePageSelect);
index 6726447c6c717a362738202d6c8943669fee0b06..c43d6d175067a90b588ad69888dd7ea785b6dea8 100644 (file)
@@ -22,9 +22,13 @@ import * as React from 'react';
 import { ButtonLink } from '../../../components/controls/buttons';
 import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks';
 import { click, waitAndUpdate } from '../../../helpers/testUtils';
-import { HomePage } from '../../../types/types';
+import { HomePage } from '../../../types/users';
 import { DEFAULT_HOMEPAGE, HomePageSelect } from '../HomePageSelect';
 
+jest.mock('../../../api/users', () => ({
+  setHomePage: jest.fn().mockResolvedValue(null)
+}));
+
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot('unchecked');
   expect(
@@ -40,20 +44,20 @@ it('should render correctly', () => {
 });
 
 it('should correctly call webservices', async () => {
-  const setHomePage = jest.fn();
+  const updateCurrentUserHomepage = jest.fn();
   const currentPage: HomePage = { type: 'MY_ISSUES' };
-  const wrapper = shallowRender({ setHomePage, currentPage });
+  const wrapper = shallowRender({ updateCurrentUserHomepage, currentPage });
 
   // Set homepage.
   click(wrapper.find(ButtonLink));
   await waitAndUpdate(wrapper);
-  expect(setHomePage).toHaveBeenLastCalledWith(currentPage);
+  expect(updateCurrentUserHomepage).toHaveBeenLastCalledWith(currentPage);
 
   // Reset.
   wrapper.setProps({ currentUser: mockLoggedInUser({ homepage: currentPage }) });
   click(wrapper.find(ButtonLink));
   await waitAndUpdate(wrapper);
-  expect(setHomePage).toHaveBeenLastCalledWith(DEFAULT_HOMEPAGE);
+  expect(updateCurrentUserHomepage).toHaveBeenLastCalledWith(DEFAULT_HOMEPAGE);
 });
 
 function shallowRender(props: Partial<HomePageSelect['props']> = {}) {
@@ -61,7 +65,7 @@ function shallowRender(props: Partial<HomePageSelect['props']> = {}) {
     <HomePageSelect
       currentPage={{ type: 'MY_PROJECTS' }}
       currentUser={mockLoggedInUser()}
-      setHomePage={jest.fn()}
+      updateCurrentUserHomepage={jest.fn()}
       {...props}
     />
   );
index 6ac87a1e2666643e20b4603529044da199704f24..caa9afc503f4aac60808815e8ae8578e3a34cbb5 100644 (file)
@@ -19,8 +19,8 @@
  */
 import { shallow, ShallowWrapper } from 'enzyme';
 import * as React from 'react';
+import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
 import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication';
-import { mockStore } from '../../../helpers/testMocks';
 import { whenLoggedIn } from '../whenLoggedIn';
 
 jest.mock('../../../helpers/handleRequiredAuthentication', () => jest.fn());
@@ -45,13 +45,21 @@ it('should not render for anonymous user', () => {
 
 function getRenderedType(wrapper: ShallowWrapper) {
   return wrapper
+    .dive()
     .dive()
     .dive()
     .type();
 }
 
 function shallowRender(isLoggedIn = true) {
-  return shallow(<UnderTest />, {
-    context: { store: mockStore({ users: { currentUser: { isLoggedIn } } }) }
-  });
+  return shallow(
+    <CurrentUserContext.Provider
+      value={{
+        currentUser: { isLoggedIn },
+        updateCurrentUserHomepage: () => {},
+        updateCurrentUserSonarLintAdSeen: () => {}
+      }}>
+      <UnderTest />
+    </CurrentUserContext.Provider>
+  );
 }
diff --git a/server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx b/server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx
deleted file mode 100644 (file)
index ab529ec..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockStore } from '../../../helpers/testMocks';
-import { CurrentUser } from '../../../types/types';
-import { withCurrentUser } from '../withCurrentUser';
-
-class X extends React.Component<{ currentUser: CurrentUser }> {
-  render() {
-    return <div />;
-  }
-}
-
-const UnderTest = withCurrentUser(X);
-
-it('should pass logged in user', () => {
-  const currentUser = { isLoggedIn: false };
-  const wrapper = shallow(<UnderTest />, {
-    context: { store: mockStore({ users: { currentUser } }) }
-  });
-  expect(wrapper.dive().type()).toBe(X);
-  expect(wrapper.dive().prop('currentUser')).toBe(currentUser);
-});
index e07a0627b7db83001e4a7ee387699ba95f18cc00..6568ccb8582ccc1161fe961df96da92061c7f33f 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
 import handleRequiredAuthentication from '../../helpers/handleRequiredAuthentication';
-import { isLoggedIn } from '../../helpers/users';
-import { CurrentUser } from '../../types/types';
+import { CurrentUser, isLoggedIn } from '../../types/users';
 import { getWrappedDisplayName } from './utils';
-import { withCurrentUser } from './withCurrentUser';
 
 export function whenLoggedIn<P>(WrappedComponent: React.ComponentType<P>) {
   class Wrapper extends React.Component<P & { currentUser: CurrentUser }> {
@@ -37,11 +36,10 @@ export function whenLoggedIn<P>(WrappedComponent: React.ComponentType<P>) {
     render() {
       if (isLoggedIn(this.props.currentUser)) {
         return <WrappedComponent {...this.props} />;
-      } else {
-        return null;
       }
+      return null;
     }
   }
 
-  return withCurrentUser(Wrapper);
+  return withCurrentUserContext(Wrapper);
 }
diff --git a/server/sonar-web/src/main/js/components/hoc/withCurrentUser.tsx b/server/sonar-web/src/main/js/components/hoc/withCurrentUser.tsx
deleted file mode 100644 (file)
index 221b3cc..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { connect } from 'react-redux';
-import { getCurrentUser, Store } from '../../store/rootReducer';
-import { CurrentUser } from '../../types/types';
-import { getWrappedDisplayName } from './utils';
-
-export function withCurrentUser<P>(
-  WrappedComponent: React.ComponentType<P & { currentUser: CurrentUser }>
-) {
-  class Wrapper extends React.Component<P & { currentUser: CurrentUser }> {
-    static displayName = getWrappedDisplayName(WrappedComponent, 'withCurrentUser');
-
-    render() {
-      return <WrappedComponent {...this.props} />;
-    }
-  }
-
-  function mapStateToProps(state: Store) {
-    return { currentUser: getCurrentUser(state) };
-  }
-
-  return connect(mapStateToProps)(Wrapper);
-}
index 69b5f81e7f5792887a09f086fd9d53e7b0d9d3c5..87c990089c26ab33fafdd7299013d5158b05dc1c 100644 (file)
@@ -18,7 +18,7 @@ exports[`should open the popup when the button is clicked 2`] = `
     onRequestClose={[Function]}
     open={true}
     overlay={
-      <Connect(withCurrentUser(SetAssigneePopup))
+      <withCurrentUserContext(SetAssigneePopup)
         onSelect={[MockFunction]}
       />
     }
@@ -61,7 +61,7 @@ exports[`should render a fallback assignee display if assignee info are not avai
     onRequestClose={[Function]}
     open={false}
     overlay={
-      <Connect(withCurrentUser(SetAssigneePopup))
+      <withCurrentUserContext(SetAssigneePopup)
         onSelect={[MockFunction]}
       />
     }
@@ -94,7 +94,7 @@ exports[`should render with the action 1`] = `
     onRequestClose={[Function]}
     open={false}
     overlay={
-      <Connect(withCurrentUser(SetAssigneePopup))
+      <withCurrentUserContext(SetAssigneePopup)
         onSelect={[MockFunction]}
       />
     }
index 5b10940ed5efaf5c09b3e389dec4660819500cc1..28260c4d74dbb931e734aa17bdaaed2b6e66b9cc 100644 (file)
 import { map } from 'lodash';
 import * as React from 'react';
 import { searchUsers } from '../../../api/users';
+import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { DropdownOverlay } from '../../../components/controls/Dropdown';
 import SearchBox from '../../../components/controls/SearchBox';
 import { translate } from '../../../helpers/l10n';
-import { isLoggedIn, isUserActive } from '../../../helpers/users';
-import { CurrentUser, UserActive, UserBase } from '../../../types/types';
+import { CurrentUser, isLoggedIn, isUserActive, UserActive, UserBase } from '../../../types/users';
 import SelectList from '../../common/SelectList';
 import SelectListItem from '../../common/SelectListItem';
-import { withCurrentUser } from '../../hoc/withCurrentUser';
 import Avatar from '../../ui/Avatar';
 
 interface Props {
@@ -121,4 +120,4 @@ export class SetAssigneePopup extends React.PureComponent<Props, State> {
   }
 }
 
-export default withCurrentUser(SetAssigneePopup);
+export default withCurrentUserContext(SetAssigneePopup);
index d1f823bac0b5a0c52b56b7c7849260ffec46be73..b8cea65d8a0c8f3538041e8611a18635b26b3d79 100644 (file)
@@ -24,7 +24,8 @@ import { getValues } from '../../api/settings';
 import { getHostUrl } from '../../helpers/urls';
 import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings';
 import { SettingsKey } from '../../types/settings';
-import { Component, LoggedInUser } from '../../types/types';
+import { Component } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import { withRouter } from '../hoc/withRouter';
 import TutorialSelectionRenderer from './TutorialSelectionRenderer';
 import { TutorialModes } from './types';
index 0cc4c1bc6e511328c8109d5f4b7514f582a4153a..bf261c639f34bbe94e36f395a25195016f93f370 100644 (file)
@@ -22,7 +22,8 @@ import EllipsisIcon from '../../components/icons/EllipsisIcon';
 import { translate } from '../../helpers/l10n';
 import { getBaseUrl } from '../../helpers/system';
 import { AlmKeys, AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings';
-import { Component, LoggedInUser } from '../../types/types';
+import { Component } from '../../types/types';
+import { LoggedInUser } from '../../types/users';
 import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial';
 import BitbucketPipelinesTutorial from './bitbucket-pipelines/BitbucketPipelinesTutorial';
 import GitHubActionTutorial from './github-action/GitHubActionTutorial';
index e9acf7bd30b7147996c8a23a0114241903a9407d..0e4c0c77b8a2e006139f4bbd3df2f7e9561564c4 100644 (file)
@@ -487,7 +487,7 @@ exports[`should render correctly: gitlab tutorial 1`] = `
 
 exports[`should render correctly: jenkins tutorial 1`] = `
 <Fragment>
-  <Connect(withAppStateContext(JenkinsTutorial))
+  <withAppStateContext(JenkinsTutorial)
     almBinding={
       Object {
         "alm": "github",
index 002724e3efc25915b28b30fba62c6893a5c48c2b..c42e621e2485a64f8a8a75455a519b628bd2282a 100644 (file)
@@ -21,7 +21,8 @@ import * as React from 'react';
 import { Button } from '../../../components/controls/buttons';
 import { translate } from '../../../helpers/l10n';
 import { AlmKeys } from '../../../types/alm-settings';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import AllSetStep from '../components/AllSetStep';
 import FinishButton from '../components/FinishButton';
 import Step from '../components/Step';
index 309dad41c80912d6b06745af177a4ef94dd7c832..e9e3f7a60cd3810547d98c50d6e5a3b967fbe9d3 100644 (file)
@@ -22,7 +22,8 @@ import { FormattedMessage } from 'react-intl';
 import { Button } from '../../../components/controls/buttons';
 import { ClipboardIconButton } from '../../../components/controls/clipboard';
 import { translate } from '../../../helpers/l10n';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import EditTokenModal from '../components/EditTokenModal';
 import SentenceWithHighlights from '../components/SentenceWithHighlights';
 
index f493f17d2163251579167dd0cc8b62c72914e353..2ac61ddcf7990dfc382f35562f3c439807fed6ba 100644 (file)
@@ -24,7 +24,8 @@ import {
   AlmSettingsInstance,
   ProjectAlmBindingResponse
 } from '../../../types/alm-settings';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import AllSetStep from '../components/AllSetStep';
 import FinishButton from '../components/FinishButton';
 import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories';
index ee9775e3823cebf7c2bad24e0ff9c245171023b9..9fa7e0f5b7a774c8c6a06441dc7ac4f014a7ddb9 100644 (file)
@@ -23,7 +23,8 @@ import { Button } from '../../../components/controls/buttons';
 import { ClipboardIconButton } from '../../../components/controls/clipboard';
 import { translate } from '../../../helpers/l10n';
 import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import SentenceWithHighlights from '../components/SentenceWithHighlights';
 import TokenStepGenerator from '../components/TokenStepGenerator';
 import { buildBitbucketCloudLink } from '../utils';
index d373131ebcbcdf82fccc344239e20e167f7073c9..77d8e3497008162407d1035d2be7f69b6bb83e8e 100644 (file)
@@ -27,7 +27,8 @@ import SimpleModal from '../../../components/controls/SimpleModal';
 import { Alert } from '../../../components/ui/Alert';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import { getUniqueTokenName } from '../utils';
 
 interface State {
index 9c84462ce6e235bf36dd9e9bc478bd9f57f18fdb..265a04b16365b6bde9c4ab5d5d9d8968944396d6 100644 (file)
@@ -21,7 +21,8 @@ import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { Button } from '../../../components/controls/buttons';
 import { translate } from '../../../helpers/l10n';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import EditTokenModal from './EditTokenModal';
 
 export interface TokenStepGeneratorProps {
index 3544d8b6e080e7f5b503d8c9a1cd85d22b168a15..efa89c8d623ee5b695264bf30f17efc7b2dca25d 100644 (file)
@@ -24,7 +24,8 @@ import {
   AlmSettingsInstance,
   ProjectAlmBindingResponse
 } from '../../../types/alm-settings';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import AllSetStep from '../components/AllSetStep';
 import Step from '../components/Step';
 import YamlFileStep from '../components/YamlFileStep';
index 590c7ab9a7d833bfd97e13f7ce2b2eabe97786b5..9eb8006ddf4dec3765aa9d597d33cc837cd9a933 100644 (file)
@@ -23,7 +23,8 @@ import { Button } from '../../../components/controls/buttons';
 import { ClipboardIconButton } from '../../../components/controls/clipboard';
 import { translate } from '../../../helpers/l10n';
 import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import SentenceWithHighlights from '../components/SentenceWithHighlights';
 import TokenStepGenerator from '../components/TokenStepGenerator';
 import { buildGithubLink } from '../utils';
index c08d7f99c41132ee146252f94b159926bd96c3a7..03506a29536a1257aae25f646db1e9f5d9dd1a33 100644 (file)
@@ -22,7 +22,8 @@ import { FormattedMessage } from 'react-intl';
 import { Button } from '../../../components/controls/buttons';
 import { ClipboardIconButton } from '../../../components/controls/clipboard';
 import { translate } from '../../../helpers/l10n';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import Step from '../components/Step';
 import TokenStepGenerator from '../components/TokenStepGenerator';
 
index bdc68c3558d70df02dc6f773881e50b15eefd644..faf5190efd75e3d8aded798191bbcc6c242b7f01 100644 (file)
@@ -20,7 +20,8 @@
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
 import { AlmKeys } from '../../../types/alm-settings';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import AllSetStep from '../components/AllSetStep';
 import { BuildTools } from '../types';
 import EnvironmentVariablesStep from './EnvironmentVariablesStep';
index bb65db801d6e251a6d2d7c8651801066732e4fb4..c0aee40c051535f88b529c0e0324ce8d0a80efb1 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { connect } from 'react-redux';
 import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
 import { translate } from '../../../helpers/l10n';
-import { getCurrentUserSetting, Store } from '../../../store/rootReducer';
-import { setCurrentUserSetting } from '../../../store/users';
 import {
   AlmKeys,
   AlmSettingsInstance,
   ProjectAlmBindingResponse
 } from '../../../types/alm-settings';
 import { AppState } from '../../../types/appstate';
-import { Component, CurrentUserSetting } from '../../../types/types';
+import { Component } from '../../../types/types';
 import AllSetStep from '../components/AllSetStep';
 import JenkinsfileStep from './JenkinsfileStep';
 import MultiBranchPipelineStep from './MultiBranchPipelineStep';
@@ -44,8 +41,6 @@ export interface JenkinsTutorialProps {
   appState: AppState;
   component: Component;
   projectBinding?: ProjectAlmBindingResponse;
-  setCurrentUserSetting: (setting: CurrentUserSetting) => void;
-  skipPreReqs: boolean;
   willRefreshAutomatically?: boolean;
 }
 
@@ -58,8 +53,6 @@ enum Steps {
   AllSet = 5
 }
 
-const USER_SETTING_SKIP_BITBUCKET_PREREQS = 'tutorials.jenkins.skipBitbucketPreReqs';
-
 export function JenkinsTutorial(props: JenkinsTutorialProps) {
   const {
     almBinding,
@@ -67,19 +60,11 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
     appState,
     component,
     projectBinding,
-    skipPreReqs,
     willRefreshAutomatically
   } = props;
   const hasSelectAlmStep = projectBinding?.alm === undefined;
   const [alm, setAlm] = React.useState<AlmKeys | undefined>(projectBinding?.alm);
-
-  let startStep;
-  if (alm) {
-    startStep = skipPreReqs ? Steps.MultiBranchPipeline : Steps.PreRequisites;
-  } else {
-    startStep = Steps.SelectAlm;
-  }
-  const [step, setStep] = React.useState(startStep);
+  const [step, setStep] = React.useState(alm ? Steps.PreRequisites : Steps.SelectAlm);
 
   return (
     <>
@@ -107,14 +92,7 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
             finished={step > Steps.PreRequisites}
             onDone={() => setStep(Steps.MultiBranchPipeline)}
             onOpen={() => setStep(Steps.PreRequisites)}
-            onChangeSkipNextTime={skip => {
-              props.setCurrentUserSetting({
-                key: USER_SETTING_SKIP_BITBUCKET_PREREQS,
-                value: skip.toString()
-              });
-            }}
             open={step === Steps.PreRequisites}
-            skipNextTime={skipPreReqs}
           />
 
           {appState.branchesEnabled ? (
@@ -169,12 +147,4 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
   );
 }
 
-const mapStateToProps = (state: Store): Pick<JenkinsTutorialProps, 'skipPreReqs'> => {
-  return {
-    skipPreReqs: getCurrentUserSetting(state, USER_SETTING_SKIP_BITBUCKET_PREREQS) === 'true'
-  };
-};
-
-const mapDispatchToProps = { setCurrentUserSetting };
-
-export default connect(mapStateToProps, mapDispatchToProps)(withAppStateContext(JenkinsTutorial));
+export default withAppStateContext(JenkinsTutorial);
index cdcca3647a8d080613529c26ff07364d2cfb40f6..0f391b0bf5311e31a10f43d0209759de8c55ea40 100644 (file)
@@ -22,7 +22,6 @@ import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
 import { rawSizes } from '../../../app/theme';
 import { Button } from '../../../components/controls/buttons';
-import Checkbox from '../../../components/controls/Checkbox';
 import ChevronRightIcon from '../../../components/icons/ChevronRightIcon';
 import { Alert } from '../../../components/ui/Alert';
 import { translate } from '../../../helpers/l10n';
@@ -34,15 +33,13 @@ export interface PreRequisitesStepProps {
   alm: AlmKeys;
   branchesEnabled: boolean;
   finished: boolean;
-  onChangeSkipNextTime: (skip: boolean) => void;
   onDone: () => void;
   onOpen: () => void;
   open: boolean;
-  skipNextTime: boolean;
 }
 
 export default function PreRequisitesStep(props: PreRequisitesStepProps) {
-  const { alm, branchesEnabled, finished, open, skipNextTime } = props;
+  const { alm, branchesEnabled, finished, open } = props;
   return (
     <Step
       finished={finished}
@@ -85,22 +82,6 @@ export default function PreRequisitesStep(props: PreRequisitesStepProps) {
           <p className="big-spacer-bottom">
             {translate('onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations')}
           </p>
-          <p className="big-spacer-bottom display-flex-center">
-            <label
-              className="cursor-pointer"
-              htmlFor="skip-prereqs"
-              onClick={() => {
-                props.onChangeSkipNextTime(!skipNextTime);
-              }}>
-              {translate('onboarding.tutorial.with.jenkins.prereqs.skip_next_time')}
-            </label>
-            <Checkbox
-              checked={skipNextTime}
-              className="little-spacer-left"
-              id="skip-prereqs"
-              onCheck={props.onChangeSkipNextTime}
-            />
-          </p>
           <Button className="big-spacer-top" onClick={props.onDone}>
             {translate('onboarding.tutorial.with.jenkins.prereqs.done')}
             <ChevronRightIcon size={rawSizes.baseFontSizeRaw} />
index beb1264b452899752dab4f541fd85da31d9c99a7..e4d121ac0fa6880cd7cad1132b79e12d36689ada 100644 (file)
@@ -98,29 +98,6 @@ it('should correctly navigate between steps', () => {
   expect(wrapper.find(WebhookStep).props().open).toBe(true);
 });
 
-it('should correctly store the user setting', () => {
-  const setCurrentUserSetting = jest.fn();
-  const wrapper = shallowRender({ setCurrentUserSetting });
-
-  wrapper.find(PreRequisitesStep).prop('onChangeSkipNextTime')(true);
-  expect(setCurrentUserSetting).toBeCalledWith({
-    key: 'tutorials.jenkins.skipBitbucketPreReqs',
-    value: 'true'
-  });
-
-  wrapper.find(PreRequisitesStep).prop('onChangeSkipNextTime')(false);
-  expect(setCurrentUserSetting).toBeCalledWith({
-    key: 'tutorials.jenkins.skipBitbucketPreReqs',
-    value: 'false'
-  });
-});
-
-it('should correctly skip the pre-reqs step if the user requested it', () => {
-  const wrapper = shallowRender({ skipPreReqs: true });
-  expect(wrapper.find(PreRequisitesStep).props().open).toBe(false);
-  expect(wrapper.find(MultiBranchPipelineStep).props().open).toBe(true);
-});
-
 it('should correctly select an ALM if no project is bound', () => {
   const wrapper = shallowRender({ projectBinding: undefined });
   expect(wrapper.find(PreRequisitesStep).exists()).toBe(false);
@@ -139,8 +116,6 @@ function shallowRender(props: Partial<JenkinsTutorialProps> = {}) {
       appState={mockAppState({ branchesEnabled: true })}
       component={mockComponent()}
       projectBinding={mockProjectBitbucketBindingResponse()}
-      setCurrentUserSetting={jest.fn()}
-      skipPreReqs={false}
       willRefreshAutomatically={true}
       {...props}
     />
index 237dd9c9aa1a134428ad1015b67f7b952e9a38df..5494dc59df8e7766df8bf0753535d6e27698853a 100644 (file)
@@ -42,11 +42,9 @@ function shallowRender(props: Partial<PreRequisitesStepProps> = {}) {
       alm={AlmKeys.BitbucketServer}
       branchesEnabled={true}
       finished={false}
-      onChangeSkipNextTime={jest.fn()}
       onDone={jest.fn()}
       onOpen={jest.fn()}
       open={false}
-      skipNextTime={true}
       {...props}
     />
   );
index eed4fce8e5d7f615e3ef0d6496572273627c6688..7c2907baa6c61d6f8c83279e4ad87cf5fb5157df 100644 (file)
@@ -15,11 +15,9 @@ exports[`should render correctly: branches not enabled 1`] = `
     alm="bitbucket"
     branchesEnabled={false}
     finished={false}
-    onChangeSkipNextTime={[Function]}
     onDone={[Function]}
     onOpen={[Function]}
     open={true}
-    skipNextTime={false}
   />
   <PipelineStep
     alm="bitbucket"
@@ -98,11 +96,9 @@ exports[`should render correctly: default 1`] = `
     alm="bitbucket"
     branchesEnabled={true}
     finished={false}
-    onChangeSkipNextTime={[Function]}
     onDone={[Function]}
     onOpen={[Function]}
     open={true}
-    skipNextTime={false}
   />
   <MultiBranchPipelineStep
     alm="bitbucket"
index e3c6038f47aaddd239fc5dad26e0e9345c89ce30..33d44479d74a526f99868a32549e6b8fd4164270 100644 (file)
@@ -63,24 +63,6 @@ exports[`should render correctly: content 1`] = `
   >
     onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations
   </p>
-  <p
-    className="big-spacer-bottom display-flex-center"
-  >
-    <label
-      className="cursor-pointer"
-      htmlFor="skip-prereqs"
-      onClick={[Function]}
-    >
-      onboarding.tutorial.with.jenkins.prereqs.skip_next_time
-    </label>
-    <Checkbox
-      checked={true}
-      className="little-spacer-left"
-      id="skip-prereqs"
-      onCheck={[MockFunction]}
-      thirdState={false}
-    />
-  </p>
   <Button
     className="big-spacer-top"
     onClick={[MockFunction]}
@@ -143,24 +125,6 @@ exports[`should render correctly: content for branches disabled 1`] = `
   >
     onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations
   </p>
-  <p
-    className="big-spacer-bottom display-flex-center"
-  >
-    <label
-      className="cursor-pointer"
-      htmlFor="skip-prereqs"
-      onClick={[Function]}
-    >
-      onboarding.tutorial.with.jenkins.prereqs.skip_next_time
-    </label>
-    <Checkbox
-      checked={true}
-      className="little-spacer-left"
-      id="skip-prereqs"
-      onCheck={[MockFunction]}
-      thirdState={false}
-    />
-  </p>
   <Button
     className="big-spacer-top"
     onClick={[MockFunction]}
@@ -226,24 +190,6 @@ exports[`should render correctly: content for branches disabled, gitlab 1`] = `
   >
     onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations
   </p>
-  <p
-    className="big-spacer-bottom display-flex-center"
-  >
-    <label
-      className="cursor-pointer"
-      htmlFor="skip-prereqs"
-      onClick={[Function]}
-    >
-      onboarding.tutorial.with.jenkins.prereqs.skip_next_time
-    </label>
-    <Checkbox
-      checked={true}
-      className="little-spacer-left"
-      id="skip-prereqs"
-      onCheck={[MockFunction]}
-      thirdState={false}
-    />
-  </p>
   <Button
     className="big-spacer-top"
     onClick={[MockFunction]}
index 16408ef15c1ba277d48d8f31d575ec95a7f16da0..5e3f818714d151463125579f0ad262534789121e 100644 (file)
@@ -19,7 +19,8 @@
  */
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
-import { Component, LoggedInUser } from '../../../types/types';
+import { Component } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import InstanceMessage from '../../common/InstanceMessage';
 import ProjectAnalysisStep from './ProjectAnalysisStep';
 import TokenStep from './TokenStep';
index 622b45ab817c0871c7154e2cb4b9b5e899fc7d55..df9e3b6386972ee00d069f6bbbf1cff294400047 100644 (file)
@@ -25,7 +25,8 @@ import { Button, DeleteButton, SubmitButton } from '../../../components/controls
 import Radio from '../../../components/controls/Radio';
 import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon';
 import { translate } from '../../../helpers/l10n';
-import { LoggedInUser, UserToken } from '../../../types/types';
+import { UserToken } from '../../../types/types';
+import { LoggedInUser } from '../../../types/users';
 import AlertErrorIcon from '../../icons/AlertErrorIcon';
 import Step from '../components/Step';
 import { getUniqueTokenName } from '../utils';
index 20bbf75cd1af205bdfa54c761d8a37e5f183e2d4..3ecf8bb1eb74ea5843a2a1df5eefefe51b90cbd2 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { change, click, submit, waitAndUpdate } from '../../../../helpers/testUtils';
-import { LoggedInUser } from '../../../../types/types';
+import { LoggedInUser } from '../../../../types/users';
 import TokenStep from '../TokenStep';
 
 jest.mock('../../../../api/user-tokens', () => ({
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/users-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/users-test.ts
new file mode 100644 (file)
index 0000000..69575d5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import { isSameHomePage } from '../users';
+
+describe('isSameHomePage', () => {
+  it('should homepage equality properly', () => {
+    expect(
+      isSameHomePage(
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch',
+          component: 'test-component'
+        },
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch',
+          component: 'test-component'
+        }
+      )
+    ).toBe(true);
+
+    expect(
+      isSameHomePage(
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch',
+          component: 'test-component'
+        },
+        {
+          type: 'ISSUES'
+        }
+      )
+    ).toBe(false);
+
+    expect(
+      isSameHomePage(
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch',
+          component: 'test-component'
+        },
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch-1',
+          component: 'test-component'
+        }
+      )
+    ).toBe(false);
+
+    expect(
+      isSameHomePage(
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch',
+          component: 'test-component'
+        },
+        {
+          type: 'APPLICATION',
+          branch: 'test-branch',
+          component: 'test-component-1'
+        }
+      )
+    ).toBe(false);
+  });
+});
index 322de67f38865c1d051854570deed130c1ff68f0..8cbf77dfcaac3f38526ab5650d79a2c6e5b1655e 100644 (file)
@@ -24,7 +24,8 @@ import SecurityHotspotIcon from '../components/icons/SecurityHotspotIcon';
 import VulnerabilityIcon from '../components/icons/VulnerabilityIcon';
 import { IssueType, RawIssue } from '../types/issues';
 import { MetricKey } from '../types/metrics';
-import { Dict, FlowLocation, Issue, TextRange, UserBase } from '../types/types';
+import { Dict, FlowLocation, Issue, TextRange } from '../types/types';
+import { UserBase } from '../types/users';
 import { ISSUE_TYPES } from './constants';
 
 interface Rule {}
index 75e09d1c4065f48a4e7ea81707eb872a4f73b830..d184c0ffb8c40d5d39723ab345aacb709ce4da71 100644 (file)
@@ -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 { UserBase } from '../../types/types';
+import { UserBase } from '../../types/users';
 
 export function mockUserBase(overrides: Partial<UserBase> = {}): UserBase {
   return {
index d94d804787a724f5d385347499c940b26cf2f4e8..3c4c452ddd7f79af43118956e4183c7774ad516f 100644 (file)
@@ -32,13 +32,11 @@ import {
   Analysis,
   AnalysisEvent,
   Condition,
-  CurrentUser,
   FlowLocation,
   Group,
   HealthType,
   IdentityProvider,
   Issue,
-  LoggedInUser,
   Measure,
   MeasureEnhanced,
   Metric,
@@ -55,9 +53,9 @@ import {
   SourceViewerFile,
   SysInfoBase,
   SysInfoCluster,
-  SysInfoStandalone,
-  User
+  SysInfoStandalone
 } from '../types/types';
+import { CurrentUser, LoggedInUser, User } from '../types/users';
 
 export function mockAlmApplication(overrides: Partial<AlmApplication> = {}): AlmApplication {
   return {
index e7aaa61ddbbb698ac60ead9d749b9d67598e256e..cfcf656469650c53292b92c4cdc2aa93004c24fc 100644 (file)
@@ -24,7 +24,8 @@ import { ComponentQualifier, isApplication, isPortfolioLike } from '../types/com
 import { MeasurePageView } from '../types/measures';
 import { GraphType } from '../types/project-activity';
 import { SecurityStandard } from '../types/security';
-import { Dict, HomePage } from '../types/types';
+import { Dict } from '../types/types';
+import { HomePage } from '../types/users';
 import { getBranchLikeQuery, isBranch, isMainBranch, isPullRequest } from './branch-like';
 import { IS_SSR } from './browser';
 import { serializeOptionalBoolean } from './query';
index a12c632704a6d84eb6cd8305adc46a47f30f21fe..1b91344d3ec728b050c742e13d1bc2f32ac3c023 100644 (file)
@@ -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 { CurrentUser, LoggedInUser, UserActive, UserBase } from '../types/types';
+import { CurrentUser, HomePage } from '../types/users';
 
 export function hasGlobalPermission(user: CurrentUser, permission: string): boolean {
   if (!user.permissions) {
@@ -26,10 +26,10 @@ export function hasGlobalPermission(user: CurrentUser, permission: string): bool
   return user.permissions.global.includes(permission);
 }
 
-export function isLoggedIn(user: CurrentUser): user is LoggedInUser {
-  return user.isLoggedIn;
-}
-
-export function isUserActive(user: UserBase): user is UserActive {
-  return user.active !== false && Boolean(user.name);
+export function isSameHomePage(a: HomePage, b: HomePage) {
+  return (
+    a.type === b.type &&
+    (a as any).branch === (b as any).branch &&
+    (a as any).component === (b as any).component
+  );
 }
diff --git a/server/sonar-web/src/main/js/store/__tests__/users-test.tsx b/server/sonar-web/src/main/js/store/__tests__/users-test.tsx
deleted file mode 100644 (file)
index fcb49ed..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { mockCurrentUser, mockLoggedInUser, mockUser } from '../../helpers/testMocks';
-import { isLoggedIn } from '../../helpers/users';
-import { CurrentUserSetting, HomePage, LoggedInUser } from '../../types/types';
-import reducer, {
-  getCurrentUser,
-  getCurrentUserSetting,
-  getUserByLogin,
-  getUsersByLogins,
-  receiveCurrentUser,
-  setCurrentUserSettingAction,
-  setHomePageAction,
-  setSonarlintAd,
-  State
-} from '../users';
-
-describe('reducer and actions', () => {
-  it('should allow to receive the current user', () => {
-    const initialState: State = createState();
-
-    const currentUser = mockCurrentUser();
-    const newState = reducer(initialState, receiveCurrentUser(currentUser));
-    expect(newState).toEqual(createState({ currentUser }));
-  });
-
-  it('should allow to set the homepage', () => {
-    const homepage: HomePage = { type: 'PROJECTS' };
-    const currentUser = mockLoggedInUser({ homepage: undefined });
-    const initialState: State = createState({ currentUser });
-
-    const newState = reducer(initialState, setHomePageAction(homepage));
-    expect(newState).toEqual(
-      createState({ currentUser: { ...currentUser, homepage } as LoggedInUser })
-    );
-  });
-
-  it('should allow to set a user setting', () => {
-    const setting1: CurrentUserSetting = { key: 'notifications.optOut', value: '1' };
-    const setting2: CurrentUserSetting = { key: 'notifications.readDate', value: '2' };
-    const setting3: CurrentUserSetting = { key: 'notifications.optOut', value: '2' };
-    const currentUser = mockLoggedInUser();
-    const initialState: State = createState({ currentUser });
-
-    const newState = reducer(initialState, setCurrentUserSettingAction(setting1));
-    expect(newState).toEqual(
-      createState({ currentUser: { ...currentUser, settings: [setting1] } as LoggedInUser })
-    );
-
-    const newerState = reducer(newState, setCurrentUserSettingAction(setting2));
-    expect(newerState).toEqual(
-      createState({
-        currentUser: { ...currentUser, settings: [setting1, setting2] } as LoggedInUser
-      })
-    );
-
-    const newestState = reducer(newerState, setCurrentUserSettingAction(setting3));
-    expect(newestState).toEqual(
-      createState({
-        currentUser: { ...currentUser, settings: [setting3, setting2] } as LoggedInUser
-      })
-    );
-  });
-
-  it('should allow to set the sonarLintAdSeen flag', () => {
-    const currentUser = mockLoggedInUser();
-    const initialState: State = createState({ currentUser });
-
-    const newState = reducer(initialState, setSonarlintAd());
-    expect(isLoggedIn(newState.currentUser) && newState.currentUser.sonarLintAdSeen).toBe(true);
-  });
-});
-
-describe('getters', () => {
-  const currentUser = mockLoggedInUser({ settings: [{ key: 'notifications.optOut', value: '1' }] });
-  const jane = mockUser({ login: 'jane', name: 'Jane Doe' });
-  const john = mockUser({ login: 'john' });
-  const state = createState({ currentUser, usersByLogin: { jane, john } });
-
-  it('getCurrentUser', () => {
-    expect(getCurrentUser(state)).toBe(currentUser);
-  });
-
-  it('getCurrentUserSetting', () => {
-    expect(getCurrentUserSetting(state, 'notifications.optOut')).toBe('1');
-    expect(getCurrentUserSetting(state, 'notifications.readDate')).toBeUndefined();
-  });
-
-  it('getUserByLogin', () => {
-    expect(getUserByLogin(state, 'jane')).toBe(jane);
-    expect(getUserByLogin(state, 'steve')).toBeUndefined();
-  });
-
-  it('getUsersByLogins', () => {
-    expect(getUsersByLogins(state, ['jane', 'john'])).toEqual([jane, john]);
-  });
-});
-
-function createState(overrides: Partial<State> = {}): State {
-  return { usersByLogin: {}, userLogins: [], currentUser: mockCurrentUser(), ...overrides };
-}
index 074d20e2a59de019077fe9b1f5bcf2f48f041fa5..885d7b25cdc1c4bb4d29e14b00422b42841adf4d 100644 (file)
  */
 import { combineReducers } from 'redux';
 import { BranchLike } from '../types/branch-like';
-import { CurrentUserSettingNames } from '../types/types';
 import branches, * as fromBranches from './branches';
 import globalMessages, * as fromGlobalMessages from './globalMessages';
-import users, * as fromUsers from './users';
 
 export type Store = {
   branches: fromBranches.State;
   globalMessages: fromGlobalMessages.State;
-  users: fromUsers.State;
 };
 
 export default combineReducers<Store>({
   branches,
-  globalMessages,
-  users
+  globalMessages
 });
 
 export function getGlobalMessages(state: Store) {
   return fromGlobalMessages.getGlobalMessages(state.globalMessages);
 }
 
-export function getCurrentUserSetting(state: Store, key: CurrentUserSettingNames) {
-  return fromUsers.getCurrentUserSetting(state.users, key);
-}
-
-export function getCurrentUser(state: Store) {
-  return fromUsers.getCurrentUser(state.users);
-}
-
 export function getBranchStatusByBranchLike(
   state: Store,
   component: string,
diff --git a/server/sonar-web/src/main/js/store/users.ts b/server/sonar-web/src/main/js/store/users.ts
deleted file mode 100644 (file)
index ebcdc85..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { uniq } from 'lodash';
-import { combineReducers, Dispatch } from 'redux';
-import * as api from '../api/users';
-import { isLoggedIn } from '../helpers/users';
-import {
-  CurrentUser,
-  CurrentUserSetting,
-  CurrentUserSettingNames,
-  Dict,
-  HomePage,
-  LoggedInUser
-} from '../types/types';
-import { ActionType } from './utils/actions';
-
-const enum Actions {
-  ReceiveCurrentUser = 'RECEIVE_CURRENT_USER',
-  SetCurrentUserSetting = 'SET_CURRENT_USER_SETTING',
-  SetHomePageAction = 'SET_HOMEPAGE',
-  SetSonarlintAd = 'SET_SONARLINT_AD'
-}
-
-type Action =
-  | ActionType<typeof receiveCurrentUser, Actions.ReceiveCurrentUser>
-  | ActionType<typeof setCurrentUserSettingAction, Actions.SetCurrentUserSetting>
-  | ActionType<typeof setHomePageAction, Actions.SetHomePageAction>
-  | ActionType<typeof setSonarlintAd, Actions.SetSonarlintAd>;
-
-export interface State {
-  usersByLogin: Dict<any>;
-  userLogins: string[];
-  currentUser: CurrentUser;
-}
-
-export function receiveCurrentUser(user: CurrentUser) {
-  return { type: Actions.ReceiveCurrentUser, user };
-}
-
-export function setHomePageAction(homepage: HomePage) {
-  return { type: Actions.SetHomePageAction, homepage };
-}
-
-export function setCurrentUserSettingAction(setting: CurrentUserSetting) {
-  return { type: Actions.SetCurrentUserSetting, setting };
-}
-
-export function setSonarlintAd() {
-  return { type: Actions.SetSonarlintAd };
-}
-
-export function setHomePage(homepage: HomePage) {
-  return (dispatch: Dispatch) => {
-    api.setHomePage(homepage).then(
-      () => {
-        dispatch(setHomePageAction(homepage));
-      },
-      () => {}
-    );
-  };
-}
-
-export function setCurrentUserSetting(setting: CurrentUserSetting) {
-  return (dispatch: Dispatch, getState: () => { users: State }) => {
-    const oldSetting = getCurrentUserSetting(getState().users, setting.key);
-    dispatch(setCurrentUserSettingAction(setting));
-    api.setUserSetting(setting).then(
-      () => {},
-      () => {
-        dispatch(setCurrentUserSettingAction({ ...setting, value: oldSetting || '' }));
-      }
-    );
-  };
-}
-
-function usersByLogin(state: State['usersByLogin'] = {}, action: Action): State['usersByLogin'] {
-  if (action.type === Actions.ReceiveCurrentUser && isLoggedIn(action.user)) {
-    return { ...state, [action.user.login]: action.user };
-  } else {
-    return state;
-  }
-}
-
-function userLogins(state: State['userLogins'] = [], action: Action): State['userLogins'] {
-  if (action.type === Actions.ReceiveCurrentUser && isLoggedIn(action.user)) {
-    return uniq([...state, action.user.login]);
-  } else {
-    return state;
-  }
-}
-
-function currentUser(
-  state: State['currentUser'] = { isLoggedIn: false },
-  action: Action
-): State['currentUser'] {
-  if (action.type === Actions.ReceiveCurrentUser) {
-    return action.user;
-  }
-  if (action.type === Actions.SetHomePageAction && isLoggedIn(state)) {
-    return { ...state, homepage: action.homepage } as LoggedInUser;
-  }
-  if (action.type === Actions.SetCurrentUserSetting && isLoggedIn(state)) {
-    let settings: CurrentUserSetting[];
-    if (state.settings) {
-      settings = [...state.settings];
-      const index = settings.findIndex(setting => setting.key === action.setting.key);
-      if (index === -1) {
-        settings.push(action.setting);
-      } else {
-        settings[index] = action.setting;
-      }
-    } else {
-      settings = [action.setting];
-    }
-    return { ...state, settings } as LoggedInUser;
-  }
-  if (action.type === Actions.SetSonarlintAd && isLoggedIn(state)) {
-    return { ...state, sonarLintAdSeen: true } as LoggedInUser;
-  }
-  return state;
-}
-
-export default combineReducers({ usersByLogin, userLogins, currentUser });
-
-export function getCurrentUser(state: State) {
-  return state.currentUser;
-}
-
-export function getCurrentUserSetting(state: State, key: CurrentUserSettingNames) {
-  let setting;
-  if (isLoggedIn(state.currentUser) && state.currentUser.settings) {
-    setting = state.currentUser.settings.find(setting => setting.key === key);
-  }
-  return setting && setting.value;
-}
-
-export function getUserByLogin(state: State, login: string) {
-  return state.usersByLogin[login];
-}
-
-export function getUsersByLogins(state: State, logins: string[]) {
-  return logins.map(login => getUserByLogin(state, login));
-}
index b444b100e698a9ca03e422464a5b702e1a00453e..5ec01bd71f0e5072b041b5c9222c06a14cc6a52c 100644 (file)
@@ -23,7 +23,8 @@ import { Location, Router } from '../components/hoc/withRouter';
 import { Store } from '../store/rootReducer';
 import { AppState } from './appstate';
 import { L10nBundle } from './l10n';
-import { CurrentUser, Dict } from './types';
+import { Dict } from './types';
+import { CurrentUser } from './users';
 
 export enum AdminPageExtension {
   GovernanceConsole = 'governance/views_console'
index 960f8bd67abcba73226c58caa9143742cc9bb33b..18f6e322f1922bff1bce150c9b603012c81aac8a 100644 (file)
@@ -17,7 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { FlowLocation, Issue, Paging, TextRange, UserBase } from './types';
+import { FlowLocation, Issue, Paging, TextRange } from './types';
+import { UserBase } from './users';
 
 export enum IssueType {
   CodeSmell = 'CODE_SMELL',
index 9d0faaeea4285263f79b23d53dc95534bf033f0c..87e3532e0803f43781aa66a0f03d42e42f361a21 100644 (file)
@@ -18,7 +18,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { BranchLike } from './branch-like';
-import { MeasureEnhanced, Metric, Status, UserBase } from './types';
+import { MeasureEnhanced, Metric, Status } from './types';
+import { UserBase } from './users';
 
 export interface QualityGateProjectStatus {
   conditions?: QualityGateProjectStatusCondition[];
index 6dfa65c923bb8576682c20b698d4e1d113008a4e..96553ddc991e5ecf034032af6985d2c8b54f9d38 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { ComponentQualifier } from './component';
-import {
-  FlowLocation,
-  IssueChangelog,
-  IssueChangelogDiff,
-  Paging,
-  TextRange,
-  UserBase
-} from './types';
+import { FlowLocation, IssueChangelog, IssueChangelogDiff, Paging, TextRange } from './types';
+import { UserBase } from './users';
 
 export enum RiskExposure {
   LOW = 'LOW',
index 2c6342be3dd3d101d4db9a7c4d5f47e530417b95..f84971c149a602352e364bab29c9e58e741b9c8a 100644 (file)
@@ -17,6 +17,9 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+
+import { UserActive, UserBase } from './users';
+
 export type Dict<T> = { [key: string]: T };
 export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 
@@ -178,40 +181,6 @@ export interface Condition {
   op?: string;
 }
 
-export interface CoveredFile {
-  key: string;
-  longName: string;
-  coveredLines: number;
-}
-
-export interface Coupon {
-  billing?: {
-    address?: string;
-    country?: string;
-    email?: string;
-    name?: string;
-    use?: string;
-  };
-  maxNcloc: number;
-  planActiveUntil: string;
-}
-
-export interface CurrentUser {
-  isLoggedIn: boolean;
-  permissions?: { global: string[] };
-  usingSonarLintConnectedMode?: boolean;
-}
-
-export interface CurrentUserSetting {
-  key: CurrentUserSettingNames;
-  value: string;
-}
-
-export type CurrentUserSettingNames =
-  | 'notifications.optOut'
-  | 'notifications.readDate'
-  | 'tutorials.jenkins.skipBitbucketPreReqs';
-
 export interface CustomMeasure {
   createdAt?: string;
   description?: string;
@@ -278,26 +247,6 @@ export interface Group {
 
 export type HealthType = 'RED' | 'YELLOW' | 'GREEN';
 
-export type HomePage =
-  | { type: 'APPLICATION'; branch: string | undefined; component: string }
-  | { type: 'ISSUES' }
-  | { type: 'MY_ISSUES' }
-  | { type: 'MY_PROJECTS' }
-  | { type: 'PORTFOLIO'; component: string }
-  | { type: 'PORTFOLIOS' }
-  | { type: 'PROJECT'; branch: string | undefined; component: string }
-  | { type: 'PROJECTS' };
-
-export type HomePageType =
-  | 'APPLICATION'
-  | 'ISSUES'
-  | 'MY_ISSUES'
-  | 'MY_PROJECTS'
-  | 'PORTFOLIO'
-  | 'PORTFOLIOS'
-  | 'PROJECT'
-  | 'PROJECTS';
-
 export interface IdentityProvider {
   backgroundColor: string;
   helpMessage?: string;
@@ -415,18 +364,6 @@ export interface LinePopup {
   open?: boolean;
 }
 
-export interface LoggedInUser extends CurrentUser, UserActive {
-  externalIdentity?: string;
-  externalProvider?: string;
-  groups: string[];
-  homepage?: HomePage;
-  isLoggedIn: true;
-  local?: boolean;
-  scmAccounts: string[];
-  settings?: CurrentUserSetting[];
-  sonarLintAdSeen?: boolean;
-}
-
 export interface Measure extends MeasureIntern {
   metric: string;
 }
@@ -838,29 +775,6 @@ export interface TextRange {
   endOffset: number;
 }
 
-export interface User extends UserBase {
-  externalIdentity?: string;
-  externalProvider?: string;
-  groups?: string[];
-  lastConnectionDate?: string;
-  local: boolean;
-  scmAccounts?: string[];
-  tokensCount?: number;
-}
-
-export interface UserActive extends UserBase {
-  active?: true;
-  name: string;
-}
-
-export interface UserBase {
-  active?: boolean;
-  avatar?: string;
-  email?: string;
-  login: string;
-  name?: string;
-}
-
 export interface UserSelected extends UserActive {
   selected: boolean;
 }
diff --git a/server/sonar-web/src/main/js/types/users.ts b/server/sonar-web/src/main/js/types/users.ts
new file mode 100644 (file)
index 0000000..5c01dc6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+export interface CurrentUser {
+  isLoggedIn: boolean;
+  permissions?: { global: string[] };
+  usingSonarLintConnectedMode?: boolean;
+}
+
+export interface LoggedInUser extends CurrentUser, UserActive {
+  externalIdentity?: string;
+  externalProvider?: string;
+  groups: string[];
+  homepage?: HomePage;
+  isLoggedIn: true;
+  local?: boolean;
+  scmAccounts: string[];
+  settings?: CurrentUserSetting[];
+  sonarLintAdSeen?: boolean;
+}
+
+export type HomePage =
+  | { type: 'APPLICATION'; branch: string | undefined; component: string }
+  | { type: 'ISSUES' }
+  | { type: 'MY_ISSUES' }
+  | { type: 'MY_PROJECTS' }
+  | { type: 'PORTFOLIO'; component: string }
+  | { type: 'PORTFOLIOS' }
+  | { type: 'PROJECT'; branch: string | undefined; component: string }
+  | { type: 'PROJECTS' };
+
+export interface CurrentUserSetting {
+  key: CurrentUserSettingNames;
+  value: string;
+}
+
+export type CurrentUserSettingNames =
+  | 'notifications.optOut'
+  | 'notifications.readDate'
+  | 'tutorials.jenkins.skipBitbucketPreReqs';
+
+export interface UserActive extends UserBase {
+  active?: true;
+  name: string;
+}
+
+export interface User extends UserBase {
+  externalIdentity?: string;
+  externalProvider?: string;
+  groups?: string[];
+  lastConnectionDate?: string;
+  local: boolean;
+  scmAccounts?: string[];
+  tokensCount?: number;
+}
+
+export interface UserBase {
+  active?: boolean;
+  avatar?: string;
+  email?: string;
+  login: string;
+  name?: string;
+}
+
+export function isUserActive(user: UserBase): user is UserActive {
+  return user.active !== false && Boolean(user.name);
+}
+
+export function isLoggedIn(user: CurrentUser): user is LoggedInUser {
+  return user.isLoggedIn;
+}
index 43c6cbcfcb2d404b0c8a164a5ea37ebdd6736a5a..67ebbdbbadb8f976fa0dde54c53a0fd982f08410 100644 (file)
@@ -3625,7 +3625,6 @@ onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner=SonarQube Scanner
 onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide=For a step by step guide on installing and configuring those plugins in Jenkins, visit the {link} documentation page.
 onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link=Analysis Prerequisites
 onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations=We recommend using the configuration in the following steps for the best results, but you can customize it as needed.
-onboarding.tutorial.with.jenkins.prereqs.skip_next_time=Don't show me the prerequisites next time
 onboarding.tutorial.with.jenkins.prereqs.done=Configure Analysis
 onboarding.tutorial.with.jenkins.multi_branch_pipeline.title=Create a Multibranch Pipeline Job
 onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro=Create a Multibranch Pipeline in order to automatically analyze all your branches and pull requests.