diff --git a/app/client/src/ce/constants/PackageConstants.ts b/app/client/src/ce/constants/PackageConstants.ts index 5d0ea3610732..a69822d2f770 100644 --- a/app/client/src/ce/constants/PackageConstants.ts +++ b/app/client/src/ce/constants/PackageConstants.ts @@ -11,6 +11,16 @@ export interface Package { modifiedBy: string; modifiedAt: string; userPermissions: string[]; + gitArtifactMetadata?: { + branchName: string; + defaultBranchName: string; + remoteUrl: string; + repoName: string; + browserSupportedUrl?: string; + isRepoPrivate?: boolean; + browserSupportedRemoteUrl: string; + defaultApplicationId: string; + }; } export type PackageMetadata = Package; diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 290161943e6a..7b254ce2da2a 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -1151,8 +1151,8 @@ export const NO_COPIED_SSH_KEY = () => "Could not copy SSH key"; // Git Branch Protection export const UPDATE = () => "Update"; export const DEFAULT_BRANCH = () => "Default branch"; -export const DEFAULT_BRANCH_DESC = () => - "This is the base branch of the app. Users launching the app from the dashboard will see the deployed version from this branch."; +export const DEFAULT_BRANCH_DESC = (artifactNoun: string) => + `This is the base branch of the ${artifactNoun}. Users launching the ${artifactNoun} from the dashboard will see the deployed version from this branch.`; export const BRANCH_PROTECTION = () => "Branch protection"; export const BRANCH_PROTECTION_DESC = () => "Protected branches enable you to enforce Git workflows. Changes to the app are not allowed in the protected branches."; @@ -1179,17 +1179,22 @@ export const BRANCH_PROTECTION_PROTECTED = () => "Protected"; export const GIT_CONNECT_SUCCESS_TITLE = () => "Successfully connected to Git"; export const GIT_CONNECT_SUCCESS_MESSAGE = () => "Now you can start collaborating with your team members by committing, merging and deploying your app"; -export const GIT_CONNECT_SUCCESS_ACTION_CONTINUE = () => - "Continue to edit application"; +export const GIT_CONNECT_SUCCESS_ACTION_CONTINUE = ( + artifactType: string = "applications", +) => `Continue to edit ${artifactType}`; export const GIT_CONNECT_SUCCESS_ACTION_SETTINGS = () => "Protect your branch"; export const GIT_CONNECT_SUCCESS_PROTECTION_MSG = () => "We recommend protecting your default branch to have a seamless collaboration."; +export const GIT_CONNECT_SUCCESS_GENERIC_MESSAGE = (artifactType: string) => + `You're all set! Your ${artifactType} is now connected to Git.`; export const GIT_CONNECT_SUCCESS_REPO_NAME = () => "Repository name"; export const GIT_CONNECT_SUCCESS_DEFAULT_BRANCH = () => "Default branch"; export const GIT_CONNECT_SUCCESS_DEFAULT_BRANCH_TOOLTIP = () => "This is the base branch of the app. Users launching the app from the dashboard will see the deployed version from this branch."; export const GIT_CONNECT_SUCCESS_PROTECTION_DOC_CTA = () => "Learn more about branch protection"; +export const GIT_CONNECT_SUCCESS_GENERIC_DOC_CTA = () => + "Learn more about how to work with Git."; // Git Connection Success end export const GENERAL = () => "General"; diff --git a/app/client/src/ce/reducers/index.tsx b/app/client/src/ce/reducers/index.tsx index e1339f84721f..0afa5653a7de 100644 --- a/app/client/src/ce/reducers/index.tsx +++ b/app/client/src/ce/reducers/index.tsx @@ -78,7 +78,10 @@ import type { ActiveField } from "reducers/uiReducers/activeFieldEditorReducer"; import type { SelectedWorkspaceReduxState } from "ee/reducers/uiReducers/selectedWorkspaceReducer"; import type { ConsolidatedPageLoadState } from "reducers/uiReducers/consolidatedPageLoadReducer"; import type { BuildingBlocksReduxState } from "reducers/uiReducers/buildingBlockReducer"; -import type { GitArtifactRootReduxState, GitGlobalReduxState } from "git"; +import type { + GitArtifactRootReduxState, + GitGlobalReduxState, +} from "git/store/types"; import { gitReducer } from "git/store"; export const reducerObject = { diff --git a/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx b/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx index eaf285c6fe9e..50c563673f74 100644 --- a/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx +++ b/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx @@ -25,8 +25,6 @@ import { import { create } from "mutative"; import { isEmpty } from "lodash"; import type { ApplicationPayload } from "entities/Application"; -import { gitConnectSuccess, type GitConnectSuccessPayload } from "git"; -import type { PayloadAction } from "@reduxjs/toolkit"; export const initialState: ApplicationsReduxState = { isSavingAppName: false, @@ -746,20 +744,6 @@ export const handlers = { isSavingNavigationSetting: false, }; }, - // git - [gitConnectSuccess.type]: ( - state: ApplicationsReduxState, - action: PayloadAction, - ) => { - return { - ...state, - currentApplication: { - ...state.currentApplication, - gitApplicationMetadata: - action.payload.responseData.gitApplicationMetadata, - }, - }; - }, }; const applicationsReducer = createReducer(initialState, handlers); diff --git a/app/client/src/ce/sagas/index.tsx b/app/client/src/ce/sagas/index.tsx index 8dbc6ba0777a..e6bb5cf321aa 100644 --- a/app/client/src/ce/sagas/index.tsx +++ b/app/client/src/ce/sagas/index.tsx @@ -51,10 +51,11 @@ import communityTemplateSagas from "sagas/CommunityTemplatesSagas"; import anvilSagas from "layoutSystems/anvil/integrations/sagas"; import ideSagas from "sagas/IDESaga"; import sendSideBySideWidgetHoverAnalyticsEventSaga from "sagas/AnalyticsSaga"; +import gitSagas from "git/sagas"; /* Sagas that are registered by a module that is designed to be independent of the core platform */ import ternSagas from "sagas/TernSaga"; -import gitSagas from "git/sagas"; +import gitApplicationSagas from "git-artifact-helpers/application/sagas"; export const sagas = [ initSagas, @@ -111,4 +112,5 @@ export const sagas = [ ideSagas, sendSideBySideWidgetHoverAnalyticsEventSaga, gitSagas, + gitApplicationSagas, ]; diff --git a/app/client/src/ce/utils/permissionHelpers.tsx b/app/client/src/ce/utils/permissionHelpers.tsx index 429422765ed0..da55328cf376 100644 --- a/app/client/src/ce/utils/permissionHelpers.tsx +++ b/app/client/src/ce/utils/permissionHelpers.tsx @@ -36,11 +36,11 @@ export enum PERMISSION_TYPE { MANAGE_ACTIONS = "manage:actions", DELETE_ACTIONS = "delete:actions", EXECUTE_ACTIONS = "execute:actions", - /* Git permissions */ - CONNECT_TO_GIT = "connectToGit:applications", - MANAGE_PROTECTED_BRANCHES = "manageProtectedBranches:applications", - MANAGE_DEFAULT_BRANCH = "manageDefaultBranches:applications", - MANAGE_AUTO_COMMIT = "manageAutoCommit:applications", + /* Git application permissions */ + GIT_APPLICATION_CONNECT = "connectToGit:applications", + GIT_APPLICATION_MANAGE_PROTECTED_BRANCHES = "manageProtectedBranches:applications", + GIT_APPLICATION_MANAGE_DEFAULT_BRANCH = "manageDefaultBranches:applications", + GIT_APPLICATION_MANAGE_AUTO_COMMIT = "manageAutoCommit:applications", } export enum LOGIC_FILTER { @@ -119,22 +119,33 @@ export const hasManageWorkspaceEnvironmentPermission = ( _permissions?: string[], ) => false; -export const hasConnectToGitPermission = (permissions: string[] = []) => { - return isPermitted(permissions, PERMISSION_TYPE.CONNECT_TO_GIT); +export const hasGitAppConnectPermission = (permissions: string[] = []) => { + return isPermitted(permissions, PERMISSION_TYPE.GIT_APPLICATION_CONNECT); }; -export const hasManageProtectedBranchesPermission = ( +export const hasGitAppManageProtectedBranchesPermission = ( permissions: string[] = [], ) => { - return isPermitted(permissions, PERMISSION_TYPE.MANAGE_PROTECTED_BRANCHES); + return isPermitted( + permissions, + PERMISSION_TYPE.GIT_APPLICATION_MANAGE_PROTECTED_BRANCHES, + ); }; -export const hasManageDefaultBranchPermission = ( +export const hasGitAppManageDefaultBranchPermission = ( permissions: string[] = [], ) => { - return isPermitted(permissions, PERMISSION_TYPE.MANAGE_DEFAULT_BRANCH); + return isPermitted( + permissions, + PERMISSION_TYPE.GIT_APPLICATION_MANAGE_DEFAULT_BRANCH, + ); }; -export const hasManageAutoCommitPermission = (permissions: string[] = []) => { - return isPermitted(permissions, PERMISSION_TYPE.MANAGE_AUTO_COMMIT); +export const hasGitAppManageAutoCommitPermission = ( + permissions: string[] = [], +) => { + return isPermitted( + permissions, + PERMISSION_TYPE.GIT_APPLICATION_MANAGE_AUTO_COMMIT, + ); }; diff --git a/app/client/src/components/common/Card.tsx b/app/client/src/components/common/Card.tsx index bc6e4a8d05bf..c3fa28779727 100644 --- a/app/client/src/components/common/Card.tsx +++ b/app/client/src/components/common/Card.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useMemo } from "react"; import styled from "styled-components"; import { Card as BlueprintCard, Classes } from "@blueprintjs/core"; import { omit } from "lodash"; @@ -8,6 +8,8 @@ import type { HTMLDivProps, ICardProps } from "@blueprintjs/core"; import { Button, type MenuItemProps } from "@appsmith/ads"; import GitConnectedBadge from "./GitConnectedBadge"; +import { GitCardBadge } from "git"; +import { useGitModEnabled } from "pages/Editor/gitSync/hooks/modHooks"; type CardProps = PropsWithChildren<{ backgroundColor: string; @@ -330,6 +332,16 @@ function Card({ title, titleTestId, }: CardProps) { + const isGitModEnabled = useGitModEnabled(); + + const gitBadge = useMemo(() => { + if (isGitModEnabled) { + return ; + } + + return ; + }, [isGitModEnabled]); + return ( - {showGitBadge && } + {showGitBadge ? gitBadge : null} ); } diff --git a/app/client/src/entities/Engine/AppEditorEngine.ts b/app/client/src/entities/Engine/AppEditorEngine.ts index d9e2151b0e9d..fc8f608f8452 100644 --- a/app/client/src/entities/Engine/AppEditorEngine.ts +++ b/app/client/src/entities/Engine/AppEditorEngine.ts @@ -3,6 +3,7 @@ import { resetEditorSuccess } from "actions/initActions"; import { fetchAllPageEntityCompletion, setupPageAction, + updateAppStore, } from "actions/pageActions"; import { executePageLoadActions, @@ -56,7 +57,6 @@ import type { Span } from "instrumentation/types"; import { endSpan, startNestedSpan } from "instrumentation/generateTraces"; import { getCurrentUser } from "selectors/usersSelectors"; import type { User } from "constants/userConstants"; -import log from "loglevel"; import { gitArtifactActions } from "git/store/gitArtifactSlice"; import { restoreRecentEntitiesRequest } from "actions/globalSearchActions"; import { @@ -74,7 +74,8 @@ import { selectGitApplicationCurrentBranch, selectGitModEnabled, } from "selectors/gitModSelectors"; -import { applicationArtifact } from "git/artifact-helpers/application"; +import { getPersistentAppStore } from "constants/AppConstants"; +import { applicationArtifact } from "git-artifact-helpers/application"; export default class AppEditorEngine extends AppEngine { constructor(mode: APP_MODE) { @@ -292,9 +293,8 @@ export default class AppEditorEngine extends AppEngine { const currentApplication: ApplicationPayload = yield select( getCurrentApplication, ); - const currentBranch: string | undefined = yield select( - selectGitApplicationCurrentBranch, - ); + const currentBranch: string | undefined = + currentApplication?.gitApplicationMetadata?.branchName; const isGitPersistBranchEnabled: boolean = yield select( isGitPersistBranchEnabledSelector, @@ -303,19 +303,23 @@ export default class AppEditorEngine extends AppEngine { if (isGitPersistBranchEnabled) { const currentUser: User = yield select(getCurrentUser); - if (currentUser?.email && currentApplication?.baseId && currentBranch) { + if (currentUser.email && currentApplication?.baseId && currentBranch) { yield setLatestGitBranchInLocal( currentUser.email, currentApplication.baseId, currentBranch, ); - } else { - log.error( - `There was an error setting the latest git branch in local - userEmail: ${!!currentUser?.email}, applicationId: ${currentApplication?.baseId}, branch: ${currentBranch}`, - ); } } + if (currentApplication?.id) { + yield put( + updateAppStore( + getPersistentAppStore(currentApplication.id, currentBranch), + ), + ); + } + const [isAnotherEditorTabOpen, currentTabs] = yield call( trackOpenEditorTabs, currentApplication.id, diff --git a/app/client/src/entities/Engine/index.ts b/app/client/src/entities/Engine/index.ts index b4eb39475753..ef04f811c927 100644 --- a/app/client/src/entities/Engine/index.ts +++ b/app/client/src/entities/Engine/index.ts @@ -1,11 +1,10 @@ import { fetchApplication } from "ee/actions/applicationActions"; -import { setAppMode, updateAppStore } from "actions/pageActions"; +import { setAppMode } from "actions/pageActions"; import type { ApplicationPayload } from "entities/Application"; import { ReduxActionErrorTypes, ReduxActionTypes, } from "ee/constants/ReduxActionConstants"; -import { getPersistentAppStore } from "constants/AppConstants"; import type { APP_MODE } from "entities/App"; import log from "loglevel"; import { call, put, select } from "redux-saga/effects"; @@ -20,7 +19,6 @@ import { updateBranchLocally } from "actions/gitSyncActions"; import { restoreIDEEditorViewMode } from "actions/ideActions"; import type { Span } from "instrumentation/types"; import { endSpan, startNestedSpan } from "instrumentation/generateTraces"; -import { selectGitApplicationCurrentBranch } from "selectors/gitModSelectors"; export interface AppEnginePayload { applicationId?: string; @@ -87,7 +85,7 @@ export default abstract class AppEngine { rootSpan: Span, ) { const loadAppDataSpan = startNestedSpan("AppEngine.loadAppData", rootSpan); - const { applicationId, basePageId, branch } = payload; + const { applicationId, basePageId } = payload; const { pages } = allResponses; const page = pages.data?.pages?.find((page) => page.baseId === basePageId); const apiCalls: boolean = yield failFastApiCalls( @@ -114,15 +112,7 @@ export default abstract class AppEngine { } const application: ApplicationPayload = yield select(getCurrentApplication); - const currentBranch: string | undefined = yield select( - selectGitApplicationCurrentBranch, - ); - yield put( - updateAppStore( - getPersistentAppStore(application.id, branch || currentBranch), - ), - ); const defaultPageId: string = yield select(getDefaultPageId); const defaultPageBaseId: string = yield select(getDefaultBasePageId); const toLoadPageId: string = page?.id || defaultPageId; diff --git a/app/client/src/git/artifact-helpers/application/applicationArtifact.ts b/app/client/src/git-artifact-helpers/application/applicationArtifact.ts similarity index 82% rename from app/client/src/git/artifact-helpers/application/applicationArtifact.ts rename to app/client/src/git-artifact-helpers/application/applicationArtifact.ts index 7337db575ac6..bcb6432d1e01 100644 --- a/app/client/src/git/artifact-helpers/application/applicationArtifact.ts +++ b/app/client/src/git-artifact-helpers/application/applicationArtifact.ts @@ -1,5 +1,5 @@ import { GitArtifactType } from "git/constants/enums"; -import type { GitArtifactDef } from "git/store/types"; +import type { GitArtifactDef } from "git/types"; export default function applicationArtifact( baseApplicationId: string, diff --git a/app/client/src/git/artifact-helpers/application/applicationStatusTransformer.ts b/app/client/src/git-artifact-helpers/application/applicationStatusTransformer.ts similarity index 100% rename from app/client/src/git/artifact-helpers/application/applicationStatusTransformer.ts rename to app/client/src/git-artifact-helpers/application/applicationStatusTransformer.ts diff --git a/app/client/src/components/gitContexts/GitApplicationContextProvider.tsx b/app/client/src/git-artifact-helpers/application/components/GitApplicationContextProvider.tsx similarity index 51% rename from app/client/src/components/gitContexts/GitApplicationContextProvider.tsx rename to app/client/src/git-artifact-helpers/application/components/GitApplicationContextProvider.tsx index 66e4012922ba..97b17cb519f7 100644 --- a/app/client/src/components/gitContexts/GitApplicationContextProvider.tsx +++ b/app/client/src/git-artifact-helpers/application/components/GitApplicationContextProvider.tsx @@ -1,11 +1,16 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { GitArtifactType, GitContextProvider } from "git"; import { getCurrentApplication, getWorkspaceIdForImport, } from "ee/selectors/applicationSelectors"; -import { hasCreateNewAppPermission } from "ee/utils/permissionHelpers"; +import { + hasGitAppConnectPermission, + hasGitAppManageAutoCommitPermission, + hasGitAppManageDefaultBranchPermission, + hasGitAppManageProtectedBranchesPermission, +} from "ee/utils/permissionHelpers"; import { fetchAllApplicationsOfWorkspace, setWorkspaceIdForImport, @@ -14,7 +19,7 @@ import { getApplicationsOfWorkspace, getCurrentAppWorkspace, } from "ee/selectors/selectedWorkspaceSelectors"; -import { applicationStatusTransformer } from "git/artifact-helpers/application"; +import applicationStatusTransformer from "../applicationStatusTransformer"; interface GitApplicationContextProviderProps { children: React.ReactNode; @@ -26,14 +31,31 @@ export default function GitApplicationContextProvider({ const dispatch = useDispatch(); const artifactType = GitArtifactType.Application; - const application = useSelector(getCurrentApplication); - const applications = useSelector(getApplicationsOfWorkspace); + const artifact = useSelector(getCurrentApplication); + const artifacts = useSelector(getApplicationsOfWorkspace); const workspace = useSelector(getCurrentAppWorkspace); const importWorkspaceId = useSelector(getWorkspaceIdForImport); - const isCreateNewApplicationPermitted = hasCreateNewAppPermission( - workspace.userPermissions, + + const isConnectPermitted = hasGitAppConnectPermission( + artifact?.userPermissions ?? [], ); + const isManageAutocommitPermitted = useMemo(() => { + return hasGitAppManageAutoCommitPermission(artifact?.userPermissions ?? []); + }, [artifact]); + + const isManageDefaultBranchPermitted = useMemo(() => { + return hasGitAppManageDefaultBranchPermission( + artifact?.userPermissions ?? [], + ); + }, [artifact]); + + const isManageProtectedBranchesPermitted = useMemo(() => { + return hasGitAppManageProtectedBranchesPermission( + artifact?.userPermissions ?? [], + ); + }, [artifact]); + const setImportWorkspaceId = useCallback(() => { dispatch( setWorkspaceIdForImport({ editorId: "", workspaceId: workspace.id }), @@ -46,13 +68,16 @@ export default function GitApplicationContextProvider({ return ( , +) { + const { artifactDef, responseData: destArtifact } = action.payload; + + if (artifactDef.artifactType !== GitArtifactType.Application) return; + + const pageId: string = yield select(getCurrentPageId); + + yield put(fetchPageAction(pageId)); + + const branch = destArtifact?.gitApplicationMetadata?.branchName; + + if (branch) { + const newUrl = addBranchParam(branch); + + history.replace(newUrl); + } + + const currentApplication: GitApplicationArtifact = yield select( + getCurrentApplication, + ); + + if (currentApplication) { + currentApplication.lastDeployedAt = new Date().toISOString(); + yield put({ + type: ReduxActionTypes.FETCH_APPLICATION_SUCCESS, + payload: currentApplication, + }); + } + + yield put( + gitArtifactActions.initGitForEditor({ + artifactDef, + artifact: destArtifact, + }), + ); +} diff --git a/app/client/src/git-artifact-helpers/application/sagas/applicationRedirectToClosestEntitySaga.ts b/app/client/src/git-artifact-helpers/application/sagas/applicationRedirectToClosestEntitySaga.ts new file mode 100644 index 000000000000..7c30c53699a0 --- /dev/null +++ b/app/client/src/git-artifact-helpers/application/sagas/applicationRedirectToClosestEntitySaga.ts @@ -0,0 +1,118 @@ +import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; +import { APP_MODE } from "entities/App"; +import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity"; +import { put, select, take } from "redux-saga/effects"; +import history from "utils/history"; +import type { Action } from "entities/Action"; +import { getActions, getJSCollections } from "ee/selectors/entitiesSelector"; +import type { JSCollectionDataState } from "ee/reducers/entityReducers/jsActionsReducer"; +import { initEditorAction } from "actions/initActions"; +import { getCurrentBasePageId } from "selectors/editorSelectors"; +import type { GitApplicationArtifact } from "git/types"; +import type { + GitArtifactPayloadAction, + GitAsyncSuccessPayload, +} from "git/store/types"; +import { GIT_BRANCH_QUERY_KEY } from "git/constants/misc"; +import { GitArtifactType } from "git/constants/enums"; + +function* applicationRedirectToClosestEntitySaga( + action: GitArtifactPayloadAction< + GitAsyncSuccessPayload + >, +) { + const { artifactDef, responseData: destArtifact } = action.payload; + + if (artifactDef.artifactType !== GitArtifactType.Application) return; + + const currentBasePageId: string = yield select(getCurrentBasePageId); + const pageExists = destArtifact.pages.find( + (page) => page.baseId === currentBasePageId, + ); + const defaultPage = destArtifact.pages.find((page) => page.isDefault); + + const url = new URL(window.location.href); + const { pathname } = url; + const entityInfo = identifyEntityFromPath(pathname); + + const branchName = destArtifact?.gitApplicationMetadata?.branchName ?? ""; + const urlParams = new URLSearchParams(); + + urlParams.set(GIT_BRANCH_QUERY_KEY, branchName); + let destinationUrl = ""; + + if (pageExists) { + destinationUrl = pathname; + } else if (defaultPage) { + destinationUrl = pathname.replace( + entityInfo.params.basePageId ?? "", + defaultPage.baseId, + ); + } + + destinationUrl += "?" + urlParams.toString(); + + if ( + destinationUrl !== + window.location.pathname + "?" + window.location.search + ) { + history.push(destinationUrl); + } + + yield put( + initEditorAction({ + basePageId: pageExists ? currentBasePageId : defaultPage?.baseId, + branch: branchName, + mode: APP_MODE.EDIT, + }), + ); + + let shouldGoToHomePage = false; + + if (!pageExists && defaultPage) { + shouldGoToHomePage = true; + } else { + // It is possible that the action does not exist in the incoming branch + // so here instead of showing the 404 page, we will navigate them to the + // home page + if ([FocusEntity.API, FocusEntity.QUERY].includes(entityInfo.entity)) { + // Wait for fetch actions success, check if action id in actions state + // or else navigate to home + yield take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS); + const actions: Action[] = yield select(getActions); + + if (!actions.find((action) => action.id === entityInfo.id)) { + shouldGoToHomePage = true; + } + } + + // Same for JS Objects + if (entityInfo.entity === FocusEntity.JS_OBJECT) { + yield take(ReduxActionTypes.FETCH_JS_ACTIONS_SUCCESS); + const jsActions: JSCollectionDataState = yield select(getJSCollections); + + if (!jsActions.find((action) => action.config.id === entityInfo.id)) { + shouldGoToHomePage = true; + } + } + } + + if (shouldGoToHomePage && defaultPage) { + // We will replace so that the user does not go back to the 404 url + const newUrl = destinationUrl.replace( + entityInfo.params.basePageId ?? "", + defaultPage.baseId, + ); + + history.replace(newUrl); + yield put( + initEditorAction({ + basePageId: defaultPage.baseId, + branch: branchName, + mode: APP_MODE.EDIT, + }), + ); + } +} + +export default applicationRedirectToClosestEntitySaga; diff --git a/app/client/src/git-artifact-helpers/application/sagas/index.ts b/app/client/src/git-artifact-helpers/application/sagas/index.ts new file mode 100644 index 000000000000..d6d98ee95b36 --- /dev/null +++ b/app/client/src/git-artifact-helpers/application/sagas/index.ts @@ -0,0 +1,21 @@ +import { all, takeLatest } from "redux-saga/effects"; +import applicationRedirectToClosestEntitySaga from "./applicationRedirectToClosestEntitySaga"; +import applicationConnectToGitSaga from "./applicationConnectToGitSaga"; +import { + gitCheckoutBranchSuccess, + gitConnectSuccess, + gitDiscardSuccess, + gitPullSuccess, +} from "git/store"; + +export default function* gitApplicationSagas() { + yield all([ + takeLatest(gitConnectSuccess.type, applicationConnectToGitSaga), + takeLatest(gitDiscardSuccess.type, applicationRedirectToClosestEntitySaga), + takeLatest( + gitCheckoutBranchSuccess.type, + applicationRedirectToClosestEntitySaga, + ), + takeLatest(gitPullSuccess.type, applicationRedirectToClosestEntitySaga), + ]); +} diff --git a/app/client/src/git/ce/components/DefaultBranch/DefaultBranchView.tsx b/app/client/src/git/ce/components/DefaultBranch/DefaultBranchView.tsx index 45f7f02dad1c..1e76143d2dc5 100644 --- a/app/client/src/git/ce/components/DefaultBranch/DefaultBranchView.tsx +++ b/app/client/src/git/ce/components/DefaultBranch/DefaultBranchView.tsx @@ -12,6 +12,7 @@ import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import noop from "lodash/noop"; import { useAppsmithEnterpriseUrl } from "git/hooks/useAppsmithEnterpriseUrl"; import type { GitBranch } from "git/types"; +import { GitArtifactType } from "git/constants/enums"; const Container = styled.div` padding-top: 8px; @@ -45,12 +46,14 @@ const StyledLink = styled(Link)` `; interface DefaultBranchViewProps { + artifactType: GitArtifactType | null; branches: GitBranch[] | null; isGitProtectedFeatureLicensed: boolean; updateDefaultBranch?: (branchName: string) => void; } function DefaultBranchView({ + artifactType = null, branches = null, isGitProtectedFeatureLicensed = false, updateDefaultBranch = noop, @@ -75,6 +78,17 @@ function DefaultBranchView({ const isUpdateDisabled = !selectedValue || selectedValue === currentDefaultBranch; + const artifactNoun = useMemo(() => { + switch (artifactType) { + case GitArtifactType.Application: + return "app"; + case GitArtifactType.Package: + return "package"; + default: + return "artifact"; + } + }, [artifactType]); + useEffect( function selectedValueOnInitEffect() { const defaultBranch = branches?.find((b) => b.default); @@ -106,7 +120,7 @@ function DefaultBranchView({ {createMessage(DEFAULT_BRANCH)} - {createMessage(DEFAULT_BRANCH_DESC)} + {createMessage(DEFAULT_BRANCH_DESC, artifactNoun)} {!isGitProtectedFeatureLicensed && ( diff --git a/app/client/src/git/ce/components/DefaultBranch/index.tsx b/app/client/src/git/ce/components/DefaultBranch/index.tsx index 60235b9c0e0c..b98de958dc34 100644 --- a/app/client/src/git/ce/components/DefaultBranch/index.tsx +++ b/app/client/src/git/ce/components/DefaultBranch/index.tsx @@ -1,12 +1,16 @@ import React from "react"; import DefaultBranchView from "./DefaultBranchView"; import useBranches from "git/hooks/useBranches"; +import { useGitContext } from "git/components/GitContextProvider"; export default function DefaultBranch() { + const { artifactDef } = useGitContext(); const { branches } = useBranches(); + const { artifactType } = artifactDef ?? {}; return ( diff --git a/app/client/src/git/components/CardBadge/index.tsx b/app/client/src/git/components/CardBadge/index.tsx new file mode 100644 index 000000000000..ab898881e1fc --- /dev/null +++ b/app/client/src/git/components/CardBadge/index.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import styled from "styled-components"; +import { Icon, Tooltip } from "@appsmith/ads"; + +import { CONNECTED_TO_GIT, createMessage } from "ee/constants/messages"; + +const Container = styled.div` + width: 24px; + height: 24px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + top: -12px; + right: -12px; + box-shadow: 0px 2px 16px rgba(0, 0, 0, 0.07); + background: var(--ads-v2-color-bg); +`; + +function CardBadge() { + return ( + + + + + + ); +} + +export default CardBadge; diff --git a/app/client/src/git/components/ConnectModal/ConnectInitialize/AddDeployKey.tsx b/app/client/src/git/components/ConnectModal/ConnectInitialize/AddDeployKey.tsx index d0a6e10e873c..549534c33228 100644 --- a/app/client/src/git/components/ConnectModal/ConnectInitialize/AddDeployKey.tsx +++ b/app/client/src/git/components/ConnectModal/ConnectInitialize/AddDeployKey.tsx @@ -32,12 +32,11 @@ import { READ_DOCS, createMessage, } from "ee/constants/messages"; -import type { GitProvider } from "./ChooseGitProvider"; import { GIT_DEMO_GIF } from "./constants"; import noop from "lodash/noop"; import CopyButton from "./CopyButton"; import type { GitApiError } from "git/store/types"; -import type { ConnectFormDataState } from "./types"; +import type { ConnectFormDataState, GitProvider } from "./types"; export const DeployedKeyContainer = styled.div` height: 36px; diff --git a/app/client/src/git/components/ConnectModal/ConnectInitialize/ChooseGitProvider.tsx b/app/client/src/git/components/ConnectModal/ConnectInitialize/ChooseGitProvider.tsx index cb379e9601f0..d7d49c7ab3cb 100644 --- a/app/client/src/git/components/ConnectModal/ConnectInitialize/ChooseGitProvider.tsx +++ b/app/client/src/git/components/ConnectModal/ConnectInitialize/ChooseGitProvider.tsx @@ -20,7 +20,7 @@ import { Text, } from "@appsmith/ads"; import styled from "styled-components"; -import { GIT_DEMO_GIF } from "./constants"; +import { GIT_DEMO_GIF, GIT_PROVIDERS } from "./constants"; import noop from "lodash/noop"; import { CHOOSE_A_GIT_PROVIDER_STEP, @@ -33,7 +33,7 @@ import { createMessage, } from "ee/constants/messages"; import log from "loglevel"; -import type { ConnectFormDataState } from "./types"; +import type { ConnectFormDataState, GitProvider } from "./types"; import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; const WellInnerContainer = styled.div` @@ -45,10 +45,6 @@ const CheckboxTextContainer = styled.div` justify-content: flex-start; `; -const GIT_PROVIDERS = ["github", "gitlab", "bitbucket", "others"] as const; - -export type GitProvider = (typeof GIT_PROVIDERS)[number]; - interface ChooseGitProviderProps { artifactType: string; isImport?: boolean; diff --git a/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.test.tsx b/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.test.tsx index 7456188ed980..8e1cb7057eb0 100644 --- a/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.test.tsx +++ b/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.test.tsx @@ -3,8 +3,8 @@ import React from "react"; import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { isValidGitRemoteUrl } from "../../utils"; import GenerateSSH from "./GenerateSSH"; -import type { GitProvider } from "./ChooseGitProvider"; import "@testing-library/jest-dom"; +import type { GitProvider } from "./types"; jest.mock("../../utils", () => ({ isValidGitRemoteUrl: jest.fn(), diff --git a/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.tsx b/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.tsx index c0def350b8ab..194dad595e70 100644 --- a/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.tsx +++ b/app/client/src/git/components/ConnectModal/ConnectInitialize/GenerateSSH.tsx @@ -31,8 +31,8 @@ import { } from "ee/constants/messages"; import { GIT_DEMO_GIF } from "./constants"; import { isValidGitRemoteUrl } from "../../utils"; -import type { GitProvider } from "./ChooseGitProvider"; import type { GitApiError } from "git/store/types"; +import type { GitProvider } from "./types"; interface GenerateSSHState { gitProvider?: GitProvider; diff --git a/app/client/src/git/components/ConnectModal/ConnectInitialize/constants.ts b/app/client/src/git/components/ConnectModal/ConnectInitialize/constants.ts index a15001af865d..3ff3daa8fe42 100644 --- a/app/client/src/git/components/ConnectModal/ConnectInitialize/constants.ts +++ b/app/client/src/git/components/ConnectModal/ConnectInitialize/constants.ts @@ -24,3 +24,10 @@ export const GIT_DEMO_GIF = { bitbucket: getAssetUrl(`${ASSETS_CDN_URL}/Bitbucket_add_a_deploykey.gif`), }, }; + +export const GIT_PROVIDERS = [ + "github", + "gitlab", + "bitbucket", + "others", +] as const; diff --git a/app/client/src/git/components/ConnectModal/ConnectInitialize/types.ts b/app/client/src/git/components/ConnectModal/ConnectInitialize/types.ts index 024375064f35..91ea222f69f2 100644 --- a/app/client/src/git/components/ConnectModal/ConnectInitialize/types.ts +++ b/app/client/src/git/components/ConnectModal/ConnectInitialize/types.ts @@ -1,4 +1,6 @@ -import type { GitProvider } from "./ChooseGitProvider"; +import type { GIT_PROVIDERS } from "./constants"; + +export type GitProvider = (typeof GIT_PROVIDERS)[number]; export interface ConnectFormDataState { gitProvider?: GitProvider; diff --git a/app/client/src/git/components/ConnectModal/ConnectModalView.tsx b/app/client/src/git/components/ConnectModal/ConnectModalView.tsx index 1732a6099ff7..fc815b29e39c 100644 --- a/app/client/src/git/components/ConnectModal/ConnectModalView.tsx +++ b/app/client/src/git/components/ConnectModal/ConnectModalView.tsx @@ -18,25 +18,25 @@ const StyledModalContent = styled(ModalContent)` interface ConnectModalViewProps extends ConnectInitializeProps { isModalOpen: boolean; - resetSSHKey: () => void; + resetConnectState: () => void; toggleModalOpen: (open: boolean) => void; } function ConnectModalView({ isModalOpen = false, - resetSSHKey = noop, + resetConnectState = noop, toggleModalOpen = noop, ...rest }: ConnectModalViewProps) { const handleModalOpenChange = useCallback( (open: boolean) => { if (!open) { - resetSSHKey(); + resetConnectState(); } toggleModalOpen(open); }, - [resetSSHKey, toggleModalOpen], + [resetConnectState, toggleModalOpen], ); return ( diff --git a/app/client/src/git/components/ConnectModal/index.tsx b/app/client/src/git/components/ConnectModal/index.tsx index 651626e933a7..a547bed8133e 100644 --- a/app/client/src/git/components/ConnectModal/index.tsx +++ b/app/client/src/git/components/ConnectModal/index.tsx @@ -10,13 +10,14 @@ import useImport from "git/hooks/useImport"; import history from "utils/history"; function ConnectModal() { - const { artifactDef, isCreateArtifactPermitted, setImportWorkspaceId } = + const { artifactDef, isConnectPermitted, setImportWorkspaceId } = useGitContext(); const { connect, connectError, isConnectLoading, isConnectModalOpen, + resetConnect, toggleConnectModal, } = useConnect(); const { toggleImportModal } = useImport(); @@ -53,10 +54,11 @@ function ConnectModal() { AnalyticsUtil.logEvent("GS_IMPORT_VIA_GIT_DURING_GC"); }, [setImportWorkspaceId, toggleConnectModal, toggleImportModal]); - const resetSSHKey = useCallback(() => { + const resetConnectState = useCallback(() => { + resetConnect(); resetFetchSSHKey(); resetGenerateSSHKey(); - }, [resetFetchSSHKey, resetGenerateSSHKey]); + }, [resetConnect, resetFetchSSHKey, resetGenerateSSHKey]); return ( diff --git a/app/client/src/git/components/ConnectSuccessModal/ConnectSuccessModalView.tsx b/app/client/src/git/components/ConnectSuccessModal/ConnectSuccessModalView.tsx index b0579dec513d..6df77ccdf0bf 100644 --- a/app/client/src/git/components/ConnectSuccessModal/ConnectSuccessModalView.tsx +++ b/app/client/src/git/components/ConnectSuccessModal/ConnectSuccessModalView.tsx @@ -8,6 +8,8 @@ import { GIT_CONNECT_SUCCESS_DEFAULT_BRANCH, GIT_CONNECT_SUCCESS_REPO_NAME, GIT_CONNECT_SUCCESS_DEFAULT_BRANCH_TOOLTIP, + GIT_CONNECT_SUCCESS_GENERIC_MESSAGE, + GIT_CONNECT_SUCCESS_GENERIC_DOC_CTA, } from "ee/constants/messages"; import { Button, @@ -25,7 +27,9 @@ import styled from "styled-components"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import { DOCS_BRANCH_PROTECTION_URL } from "constants/ThirdPartyConstants"; import noop from "lodash/noop"; +import type { GitArtifactType } from "git/constants/enums"; import { GitSettingsTab } from "git/constants/enums"; +import { singular } from "pluralize"; const TitleText = styled(Text)` flex: 1; @@ -53,67 +57,6 @@ function ConnectionSuccessTitle() { ); } -interface ConnectSuccessContentProps { - repoName: string | null; - defaultBranch: string | null; -} - -function ConnectSuccessContent({ - defaultBranch, - repoName, -}: ConnectSuccessContentProps) { - return ( - <> -
-
-
- - - {createMessage(GIT_CONNECT_SUCCESS_REPO_NAME)} - -
- {repoName || "-"} -
-
-
- - - {createMessage(GIT_CONNECT_SUCCESS_DEFAULT_BRANCH)} - - - - -
- {defaultBranch || "-"} -
-
-
- - {createMessage(GIT_CONNECT_SUCCESS_PROTECTION_MSG)} - -
- - - {createMessage(GIT_CONNECT_SUCCESS_PROTECTION_DOC_CTA)} - - - - ); -} - const StyledModalContent = styled(ModalContent)` &&& { width: 640px; @@ -125,8 +68,10 @@ const StyledModalContent = styled(ModalContent)` `; export interface ConnectSuccessModalViewProps { + artifactType: GitArtifactType | null; defaultBranch: string | null; isConnectSuccessModalOpen: boolean; + showProtectedBranchesInfo: boolean; remoteUrl: string | null; repoName: string | null; toggleConnectSuccessModal: (open: boolean) => void; @@ -137,10 +82,12 @@ export interface ConnectSuccessModalViewProps { } function ConnectSuccessModalView({ + artifactType = null, defaultBranch = null, isConnectSuccessModalOpen = false, remoteUrl = null, repoName = null, + showProtectedBranchesInfo = false, toggleConnectSuccessModal = noop, toggleSettingsModal = noop, }: ConnectSuccessModalViewProps) { @@ -167,26 +114,99 @@ function ConnectSuccessModalView({ - +
+
+
+ + + {createMessage(GIT_CONNECT_SUCCESS_REPO_NAME)} + +
+ {repoName || "-"} +
+
+
+ + + {createMessage(GIT_CONNECT_SUCCESS_DEFAULT_BRANCH)} + + + + +
+ {defaultBranch || "-"} +
+
+ {showProtectedBranchesInfo ? ( + <> +
+ + {createMessage(GIT_CONNECT_SUCCESS_PROTECTION_MSG)} + +
+ + + {createMessage(GIT_CONNECT_SUCCESS_PROTECTION_DOC_CTA)} + + + + ) : ( + <> +
+ + {createMessage( + GIT_CONNECT_SUCCESS_GENERIC_MESSAGE, + singular(artifactType ?? ""), + )} + +
+ + + {createMessage(GIT_CONNECT_SUCCESS_GENERIC_DOC_CTA)} + + + + )}
- + {showProtectedBranchesInfo ? ( + + ) : null}
diff --git a/app/client/src/git/components/ConnectSuccessModal/index.tsx b/app/client/src/git/components/ConnectSuccessModal/index.tsx index c492e5aa4bb3..a943f9db33fc 100644 --- a/app/client/src/git/components/ConnectSuccessModal/index.tsx +++ b/app/client/src/git/components/ConnectSuccessModal/index.tsx @@ -3,8 +3,11 @@ import ConnectSuccessModalView from "./ConnectSuccessModalView"; import useMetadata from "git/hooks/useMetadata"; import useConnect from "git/hooks/useConnect"; import useSettings from "git/hooks/useSettings"; +import { useGitContext } from "../GitContextProvider"; function ConnectSuccessModal() { + const { artifactDef, isManageProtectedBranchesPermitted } = useGitContext(); + const artifactType = artifactDef?.artifactType ?? null; const { isConnectSuccessModalOpen, toggleConnectSuccessModal } = useConnect(); const { toggleSettingsModal } = useSettings(); @@ -16,10 +19,12 @@ function ConnectSuccessModal() { return ( diff --git a/app/client/src/git/components/DangerZone/index.tsx b/app/client/src/git/components/DangerZone/index.tsx index 4d22c39076c1..9f8b41b3f594 100644 --- a/app/client/src/git/components/DangerZone/index.tsx +++ b/app/client/src/git/components/DangerZone/index.tsx @@ -1,6 +1,5 @@ import useAutocommit from "git/hooks/useAutocommit"; import useDisconnect from "git/hooks/useDisconnect"; -import useGitPermissions from "git/hooks/useGitPermissions"; import useSettings from "git/hooks/useSettings"; import React, { useCallback } from "react"; import DangerZoneView from "./DangerZoneView"; @@ -8,10 +7,13 @@ import useMetadata from "git/hooks/useMetadata"; import { useGitContext } from "../GitContextProvider"; function DangerZone() { - const { artifact, artifactDef } = useGitContext(); + const { + artifact, + artifactDef, + isConnectPermitted, + isManageAutocommitPermitted, + } = useGitContext(); const { closeDisconnectModal, openDisconnectModal } = useDisconnect(); - const { isConnectPermitted, isManageAutocommitPermitted } = - useGitPermissions(); const { isAutocommitEnabled, isToggleAutocommitLoading, diff --git a/app/client/src/git/components/GitContextProvider/index.tsx b/app/client/src/git/components/GitContextProvider/index.tsx index 8154a1c67dec..88159a8d7048 100644 --- a/app/client/src/git/components/GitContextProvider/index.tsx +++ b/app/client/src/git/components/GitContextProvider/index.tsx @@ -1,21 +1,23 @@ import React, { createContext, useContext, useMemo } from "react"; import type { GitArtifactType } from "git/constants/enums"; -import type { ApplicationPayload } from "entities/Application"; import type { FetchStatusResponseData } from "git/requests/fetchStatusRequest.types"; -import type { GitArtifactDef } from "git/store/types"; -import type { StatusTreeStruct } from "../StatusChanges/types"; +import type { GitArtifact, GitArtifactDef } from "git/types"; +import type { StatusTreeStruct } from "git/components/StatusChanges/types"; import type { Workspace } from "ee/constants/workspaceConstants"; import { noop } from "lodash"; export interface GitContextValue { artifactDef: GitArtifactDef | null; - artifact: ApplicationPayload | null; - artifacts: ApplicationPayload[] | null; + artifact: GitArtifact | null; + artifacts: GitArtifact[] | null; fetchArtifacts: () => void; workspace: Workspace | null; setImportWorkspaceId: () => void; importWorkspaceId: string | null; - isCreateArtifactPermitted: boolean; + isConnectPermitted: boolean; + isManageAutocommitPermitted: boolean; + isManageDefaultBranchPermitted: boolean; + isManageProtectedBranchesPermitted: boolean; statusTransformer: ( status: FetchStatusResponseData, ) => StatusTreeStruct[] | null; @@ -33,8 +35,8 @@ interface GitContextProviderProps { // artifact artifactType: GitArtifactType | null; baseArtifactId: string | null; - artifact: ApplicationPayload | null; - artifacts: ApplicationPayload[] | null; + artifact: GitArtifact | null; + artifacts: GitArtifact[] | null; fetchArtifacts: () => void; // workspace @@ -45,7 +47,10 @@ interface GitContextProviderProps { importWorkspaceId: string | null; // permissions - isCreateArtifactPermitted: boolean; + isConnectPermitted: boolean; + isManageAutocommitPermitted: boolean; + isManageDefaultBranchPermitted: boolean; + isManageProtectedBranchesPermitted: boolean; // artifactspecific functions statusTransformer: ( @@ -66,7 +71,10 @@ export default function GitContextProvider({ children, fetchArtifacts = noop, importWorkspaceId = null, - isCreateArtifactPermitted = false, + isConnectPermitted = false, + isManageAutocommitPermitted = false, + isManageDefaultBranchPermitted = false, + isManageProtectedBranchesPermitted = false, setImportWorkspaceId = noop, statusTransformer = NULL_NOOP, workspace = null, @@ -88,7 +96,10 @@ export default function GitContextProvider({ workspace, setImportWorkspaceId, importWorkspaceId, - isCreateArtifactPermitted, + isConnectPermitted, + isManageAutocommitPermitted, + isManageDefaultBranchPermitted, + isManageProtectedBranchesPermitted, statusTransformer, }), [ @@ -99,7 +110,10 @@ export default function GitContextProvider({ workspace, setImportWorkspaceId, importWorkspaceId, - isCreateArtifactPermitted, + isConnectPermitted, + isManageAutocommitPermitted, + isManageDefaultBranchPermitted, + isManageProtectedBranchesPermitted, statusTransformer, ], ); diff --git a/app/client/src/git/components/ImportModal/index.tsx b/app/client/src/git/components/ImportModal/index.tsx index 8192fa9e88a7..2dd82b71bbad 100644 --- a/app/client/src/git/components/ImportModal/index.tsx +++ b/app/client/src/git/components/ImportModal/index.tsx @@ -41,7 +41,7 @@ function ImportModal() { onGenerateSSHKey={fetchGlobalSSHKey} onOpenImport={null} onSubmit={onSubmit} - resetSSHKey={resetGlobalSSHKey} + resetConnectState={resetGlobalSSHKey} sshPublicKey={sshPublicKey} toggleModalOpen={toggleImportModal} /> diff --git a/app/client/src/git/components/OpsModal/TabDeploy/index.tsx b/app/client/src/git/components/OpsModal/TabDeploy/index.tsx index 44bd8ee91cd2..aa4c47cf23eb 100644 --- a/app/client/src/git/components/OpsModal/TabDeploy/index.tsx +++ b/app/client/src/git/components/OpsModal/TabDeploy/index.tsx @@ -7,6 +7,7 @@ import useCommit from "git/hooks/useCommit"; import useDiscard from "git/hooks/useDiscard"; import usePull from "git/hooks/usePull"; import useStatus from "git/hooks/useStatus"; +import type { GitApplicationArtifact } from "git/types"; export default function TabDeploy() { const { artifact } = useGitContext(); @@ -21,7 +22,9 @@ export default function TabDeploy() { const { currentBranch } = useBranches(); const { metadata } = useMetadata(); - const lastDeployedAt = artifact?.lastDeployedAt ?? null; + // ! git tagging: need to handle last deplyed here when tagging is implemented + const lastDeployedAt = + (artifact as GitApplicationArtifact)?.lastDeployedAt ?? null; const isPullFailing = !!pullError; const statusIsClean = status?.isClean ?? false; const statusBehindCount = status?.behindCount ?? 0; diff --git a/app/client/src/git/components/QuickActions/index.tsx b/app/client/src/git/components/QuickActions/index.tsx index 5ec638e8aa8d..d745ffc48996 100644 --- a/app/client/src/git/components/QuickActions/index.tsx +++ b/app/client/src/git/components/QuickActions/index.tsx @@ -1,7 +1,6 @@ import React from "react"; import QuickActionsView from "./QuickActionsView"; import useStatusChangeCount from "./hooks/useStatusChangeCount"; -import useGitPermissions from "git/hooks/useGitPermissions"; import useAutocommit from "git/hooks/useAutocommit"; import useSettings from "git/hooks/useSettings"; import useConnect from "git/hooks/useConnect"; @@ -13,15 +12,16 @@ import useBranches from "git/hooks/useBranches"; import useConnected from "git/hooks/useConnected"; import useProtectedMode from "git/hooks/useProtectedMode"; import useInit from "git/hooks/useInit"; +import { useGitContext } from "../GitContextProvider"; function QuickActions() { + const { isConnectPermitted } = useGitContext(); const isConnected = useConnected(); const { toggleOpsModal } = useOps(); const { isFetchStatusLoading, status } = useStatus(); const { isPullLoading, pull, pullError } = usePull(); const { discard, isDiscardLoading } = useDiscard(); const isProtectedMode = useProtectedMode(); - const { isConnectPermitted } = useGitPermissions(); const { isAutocommitEnabled, isAutocommitPolling, diff --git a/app/client/src/git/components/RepoLimitErrorModal/RepoLimitErrorModalView.tsx b/app/client/src/git/components/RepoLimitErrorModal/RepoLimitErrorModalView.tsx index 95d18bf4b52a..d5cee2da3e10 100644 --- a/app/client/src/git/components/RepoLimitErrorModal/RepoLimitErrorModalView.tsx +++ b/app/client/src/git/components/RepoLimitErrorModal/RepoLimitErrorModalView.tsx @@ -22,11 +22,15 @@ import { REVOKE_ACCESS, REVOKE_EXISTING_REPOSITORIES, } from "ee/constants/messages"; -import type { ApplicationPayload } from "entities/Application"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import type { GitArtifact, GitArtifactDef } from "git/store/types"; import { noop } from "lodash"; -import { applicationArtifact } from "git/artifact-helpers/application"; +import type { + GitApplicationArtifact, + GitArtifact, + GitArtifactDef, + GitPackageArtifact, +} from "git/types"; +import { applicationArtifact } from "git-artifact-helpers/application"; const StyledModalContent = styled(ModalContent)` &&& { @@ -83,15 +87,17 @@ function RepoLimitErrorModalView({ }: RepoLimitErrorModalViewProps) { const gitConnectedArtifacts = useMemo(() => { return ( - artifacts?.filter((application: ApplicationPayload) => { - const data = application.gitApplicationMetadata; + artifacts?.filter((artifact: GitArtifact) => { + const gitMetadata = + (artifact as GitApplicationArtifact).gitApplicationMetadata || + (artifact as GitPackageArtifact).gitArtifactMetadata; return ( - data && - data.remoteUrl && - data.branchName && - data.repoName && - data.isRepoPrivate + gitMetadata && + gitMetadata.remoteUrl && + gitMetadata.branchName && + gitMetadata.repoName && + gitMetadata.isRepoPrivate ); }) ?? [] ); @@ -184,22 +190,22 @@ function RepoLimitErrorModalView({ - {gitConnectedArtifacts.map((application) => { - const { gitApplicationMetadata } = application; + {gitConnectedArtifacts.map((artifact) => { + const gitMetadata = + (artifact as GitApplicationArtifact).gitApplicationMetadata || + (artifact as GitPackageArtifact).gitArtifactMetadata; return (
- {application.name} + {artifact.name} - - {gitApplicationMetadata?.remoteUrl} - + {gitMetadata?.remoteUrl}
); diff --git a/app/client/src/pages/Editor/gitSync/hooks/gitPermissionHooks.ts b/app/client/src/pages/Editor/gitSync/hooks/gitPermissionHooks.ts index e788a9aaf880..d22f6e41da3b 100644 --- a/app/client/src/pages/Editor/gitSync/hooks/gitPermissionHooks.ts +++ b/app/client/src/pages/Editor/gitSync/hooks/gitPermissionHooks.ts @@ -1,22 +1,22 @@ import { useSelector } from "react-redux"; import { - hasConnectToGitPermission, - hasManageProtectedBranchesPermission, - hasManageDefaultBranchPermission, - hasManageAutoCommitPermission, + hasGitAppConnectPermission, + hasGitAppManageAutoCommitPermission, + hasGitAppManageDefaultBranchPermission, + hasGitAppManageProtectedBranchesPermission, } from "ee/utils/permissionHelpers"; import { getCurrentApplication } from "ee/selectors/applicationSelectors"; export const useHasConnectToGitPermission = () => { const currentApplication = useSelector(getCurrentApplication); - return hasConnectToGitPermission(currentApplication?.userPermissions); + return hasGitAppConnectPermission(currentApplication?.userPermissions); }; export const useHasManageProtectedBranchesPermission = () => { const currentApplication = useSelector(getCurrentApplication); - return hasManageProtectedBranchesPermission( + return hasGitAppManageProtectedBranchesPermission( currentApplication?.userPermissions, ); }; @@ -24,11 +24,15 @@ export const useHasManageProtectedBranchesPermission = () => { export const useHasManageDefaultBranchPermission = () => { const currentApplication = useSelector(getCurrentApplication); - return hasManageDefaultBranchPermission(currentApplication?.userPermissions); + return hasGitAppManageDefaultBranchPermission( + currentApplication?.userPermissions, + ); }; export const useHasManageAutoCommitPermission = () => { const currentApplication = useSelector(getCurrentApplication); - return hasManageAutoCommitPermission(currentApplication?.userPermissions); + return hasGitAppManageAutoCommitPermission( + currentApplication?.userPermissions, + ); }; diff --git a/app/client/src/pages/Editor/gitSync/hooks/modHooks.ts b/app/client/src/pages/Editor/gitSync/hooks/modHooks.ts index e4d4b7fb4b7a..6b6a18a03db0 100644 --- a/app/client/src/pages/Editor/gitSync/hooks/modHooks.ts +++ b/app/client/src/pages/Editor/gitSync/hooks/modHooks.ts @@ -10,7 +10,7 @@ import { useGitProtectedMode as useGitProtectedModeNew, useGitCurrentBranch as useGitCurrentBranchNew, useGitConnected as useGitConnectedNew, -} from "git"; +} from "git/hooks"; import { selectGitModEnabled } from "selectors/gitModSelectors"; export function useGitModEnabled() { diff --git a/app/client/src/pages/UserProfile/index.tsx b/app/client/src/pages/UserProfile/index.tsx index f77d74fedb50..d7ec1f27aad7 100644 --- a/app/client/src/pages/UserProfile/index.tsx +++ b/app/client/src/pages/UserProfile/index.tsx @@ -10,10 +10,8 @@ import { BackButton } from "components/utils/helperComponents"; import { useDispatch } from "react-redux"; import { fetchGlobalGitConfigInit } from "actions/gitSyncActions"; import { useGitModEnabled } from "pages/Editor/gitSync/hooks/modHooks"; -import { - fetchGitGlobalProfile, - GitGlobalProfile as GitGlobalProfileNew, -} from "git"; +import { GitGlobalProfile as GitGlobalProfileNew } from "git"; +import { gitFetchGlobalProfile } from "git/store"; function GitGlobalProfile() { const isGitModEnabled = useGitModEnabled(); @@ -64,7 +62,7 @@ function UserProfile() { useEffect( function fetchGlobalGitConfigOnInitEffect() { if (isGitModEnabled) { - dispatch(fetchGitGlobalProfile()); + dispatch(gitFetchGlobalProfile()); } else { dispatch(fetchGlobalGitConfigInit()); } diff --git a/app/client/src/pages/common/ImportModal.tsx b/app/client/src/pages/common/ImportModal.tsx index 12cefc5dec8c..5eb89e3d2daf 100644 --- a/app/client/src/pages/common/ImportModal.tsx +++ b/app/client/src/pages/common/ImportModal.tsx @@ -31,7 +31,7 @@ import useMessages from "ee/hooks/importModal/useMessages"; import useMethods from "ee/hooks/importModal/useMethods"; import { getIsAnvilLayoutEnabled } from "layoutSystems/anvil/integrations/selectors"; import { useGitModEnabled } from "pages/Editor/gitSync/hooks/modHooks"; -import { toggleGitImportModal } from "git"; +import { gitToggleImportModal } from "git/store"; const TextWrapper = styled.div` padding: 0; @@ -228,7 +228,7 @@ function ImportModal(props: ImportModalProps) { if (isGitModEnabled) { dispatch( - toggleGitImportModal({ + gitToggleImportModal({ open: true, }), ); diff --git a/app/client/src/sagas/ErrorSagas.tsx b/app/client/src/sagas/ErrorSagas.tsx index 381668da3e01..7e3a79874984 100644 --- a/app/client/src/sagas/ErrorSagas.tsx +++ b/app/client/src/sagas/ErrorSagas.tsx @@ -136,21 +136,22 @@ export function* validateResponse( if ( SERVER_ERROR_CODES.INCORRECT_BINDING_LIST_OF_WIDGET.includes( - response.responseMeta.error.code, + response.responseMeta?.error?.code, ) ) { - throw new IncorrectBindingError(response.responseMeta.error.message); + throw new IncorrectBindingError(response.responseMeta?.error?.message); } yield put({ type: ReduxActionErrorTypes.API_ERROR, payload: { - error: new Error(response.responseMeta.error.message), + error: new Error(response.responseMeta?.error?.message), logToSentry, show, }, }); - throw Error(response.responseMeta.error.message); + + throw Error(response.responseMeta?.error?.message); } export function getResponseErrorMessage(response: ApiResponse) { diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 39bd1d46c1b6..b4705c273d4f 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -897,9 +897,7 @@ export const getJSCollectionDataById = createSelector( export const getJSCollectionDataByBaseId = createSelector( [ getJSCollections, - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state: AppState, baseCollectionId: any) => baseCollectionId, + (state: AppState, baseCollectionId: string) => baseCollectionId, ], (jsActions, baseCollectionId) => { const action = jsActions.find( diff --git a/app/client/src/selectors/gitModSelectors.ts b/app/client/src/selectors/gitModSelectors.ts index 5576f5ffe624..84494b2b3e19 100644 --- a/app/client/src/selectors/gitModSelectors.ts +++ b/app/client/src/selectors/gitModSelectors.ts @@ -1,6 +1,3 @@ -// temp file will be removed after git mod is fully rolled out - -import { selectFeatureFlags } from "ee/selectors/featureFlagsSelectors"; import { createSelector } from "reselect"; import { getCurrentGitBranch, @@ -12,20 +9,22 @@ import { selectGitProtectedMode as selectGitProtectedModeNew, selectGitOpsModalOpen as selectGitOpsModalOpenNew, selectGitConnectModalOpen as selectGitConnectModalOpenNew, -} from "git"; -import { - getCurrentBaseApplicationId, - previewModeSelector, -} from "./editorSelectors"; -import { applicationArtifact } from "git/artifact-helpers/application"; +} from "git/store/selectors"; +import type { AppState } from "ee/reducers"; +import { applicationArtifact } from "git-artifact-helpers/application"; export const selectGitModEnabled = createSelector( - selectFeatureFlags, + (state: AppState) => { + return { + ...state.ui.users.featureFlag.data, + ...state.ui.users.featureFlag.overriddenFlags, + }; + }, (featureFlags) => featureFlags.release_git_modularisation_enabled ?? false, ); export const selectGitApplicationArtifactDef = createSelector( - getCurrentBaseApplicationId, + (state: AppState) => state.entities.pageList.baseApplicationId || "", (baseApplicationId) => applicationArtifact(baseApplicationId), ); @@ -50,7 +49,8 @@ export const selectGitApplicationProtectedMode = createSelector( ); export const selectCombinedPreviewMode = createSelector( - previewModeSelector, + // need to do this to avoid circular dependency + (state: AppState) => state.ui.editor.isPreviewMode, selectGitApplicationProtectedMode, (isPreviewMode, isProtectedMode) => isPreviewMode || isProtectedMode, );