diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitPersistBranch_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/GitPersistBranch_spec.ts new file mode 100644 index 000000000000..a0bbf7d0817a --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitPersistBranch_spec.ts @@ -0,0 +1,45 @@ +import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags"; +import { + agHelper, + gitSync, + homePage, +} from "../../../../support/Objects/ObjectsCore"; + +let wsName: string; +let appName: string; +let repoName: string; + +describe( + "Git Persist Branch", + { + tags: ["@tag.Git", "@tag.GitPersistBranch"], + }, + function () { + before(() => { + agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + wsName = "GitPB-" + uid; + appName = "GitPB1-" + uid; + homePage.CreateNewWorkspace(wsName, true); + homePage.CreateAppInWorkspace(wsName, appName); + gitSync.CreateNConnectToGit("test-git-perssit-branch", true, true); + cy.get("@gitRepoName").then((resRepoName) => { + repoName = resRepoName.toString(); + homePage.NavigateToHome(); + }); + }); + }); + it("Check if branch persist after changing branch and exiting the app", function () { + featureFlagIntercept({ release_git_persist_branch_enabled: true }, true); + homePage.EditAppFromAppHover(appName); + gitSync.CreateGitBranch("b1", false); + cy.get("@gitbranchName").then((resBranchName) => { + const branchName = resBranchName.toString(); + homePage.NavigateToHome(); + homePage.EditAppFromAppHover(appName); + gitSync.AssertBranchName(branchName); + gitSync.AssertBranchNameInUrl(branchName); + }); + }); + }, +); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitConnectV2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitConnectV2_spec.ts index 26cbff86c2af..c157464dbe07 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitConnectV2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Git/GitSync/GitConnectV2_spec.ts @@ -1,4 +1,3 @@ -import { featureFlagIntercept } from "../../../../../support/Objects/FeatureFlags"; import * as _ from "../../../../../support/Objects/ObjectsCore"; import EditorNavigation, { EntityType, diff --git a/app/client/cypress/support/Pages/GitSync.ts b/app/client/cypress/support/Pages/GitSync.ts index 1aa7589ba7c7..47f2e8cf70a1 100644 --- a/app/client/cypress/support/Pages/GitSync.ts +++ b/app/client/cypress/support/Pages/GitSync.ts @@ -453,4 +453,16 @@ export class GitSync { } this.CloseGitSyncModal(); } + + public AssertBranchName(branch: string) { + this.agHelper.AssertElementVisibility(this._branchButton); + this.agHelper.AssertContains(branch); + } + + public AssertBranchNameInUrl(branch: string) { + cy.location("search") + .then((searchParams) => new URLSearchParams(searchParams)) + .invoke("get", "branch") + .should("equal", branch); + } } diff --git a/app/client/src/ce/entities/FeatureFlag.ts b/app/client/src/ce/entities/FeatureFlag.ts index 311a1b2037e5..5b3919ce5dba 100644 --- a/app/client/src/ce/entities/FeatureFlag.ts +++ b/app/client/src/ce/entities/FeatureFlag.ts @@ -37,6 +37,7 @@ export const FEATURE_FLAG = { rollout_side_by_side_enabled: "rollout_side_by_side_enabled", release_layout_conversion_enabled: "release_layout_conversion_enabled", release_anvil_toggle_enabled: "release_anvil_toggle_enabled", + release_git_persist_branch_enabled: "release_git_persist_branch_enabled", release_ide_animations_enabled: "release_ide_animations_enabled", } as const; @@ -71,6 +72,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = { rollout_side_by_side_enabled: false, release_layout_conversion_enabled: false, release_anvil_toggle_enabled: false, + release_git_persist_branch_enabled: false, release_ide_animations_enabled: false, }; diff --git a/app/client/src/entities/Engine/AppEditorEngine.ts b/app/client/src/entities/Engine/AppEditorEngine.ts index b36e86b4e08c..a7520a0d03ea 100644 --- a/app/client/src/entities/Engine/AppEditorEngine.ts +++ b/app/client/src/entities/Engine/AppEditorEngine.ts @@ -33,7 +33,10 @@ import { reportSWStatus, waitForWidgetConfigBuild, } from "sagas/InitSagas"; -import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; +import { + getCurrentGitBranch, + isGitPersistBranchEnabledSelector, +} from "selectors/gitSyncSelectors"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import history from "utils/history"; import type { AppEnginePayload } from "."; @@ -50,7 +53,7 @@ import { } from "ee/sagas/userSagas"; import { getFirstTimeUserOnboardingComplete } from "selectors/onboardingSelectors"; import { isAirgapped } from "ee/utils/airgapHelpers"; -import { getAIPromptTriggered } from "utils/storage"; +import { getAIPromptTriggered, setLatestGitBranchInLocal } from "utils/storage"; import { trackOpenEditorTabs } from "../../utils/editor/browserTabsTracking"; import { EditorModes } from "components/editorComponents/CodeEditor/EditorConfig"; import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas"; @@ -68,6 +71,9 @@ import { import { getCurrentApplication } from "ee/selectors/applicationSelectors"; import type { Span } from "@opentelemetry/api"; import { endSpan, startNestedSpan } from "UITelemetry/generateTraces"; +import { getCurrentUser } from "selectors/usersSelectors"; +import type { User } from "constants/userConstants"; +import log from "loglevel"; export default class AppEditorEngine extends AppEngine { constructor(mode: APP_MODE) { @@ -269,6 +275,27 @@ export default class AppEditorEngine extends AppEngine { getCurrentApplication, ); + const isGitPersistBranchEnabled: boolean = yield select( + isGitPersistBranchEnabledSelector, + ); + + if (isGitPersistBranchEnabled) { + const currentUser: User = yield select(getCurrentUser); + const currentBranch: string = yield select(getCurrentGitBranch); + + 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}`, + ); + } + } + const [isAnotherEditorTabOpen, currentTabs] = yield call( trackOpenEditorTabs, currentApplication.id, diff --git a/app/client/src/pages/Applications/ApplicationCard.tsx b/app/client/src/pages/Applications/ApplicationCard.tsx index 4e6d0890caa7..484b7bb6eacf 100644 --- a/app/client/src/pages/Applications/ApplicationCard.tsx +++ b/app/client/src/pages/Applications/ApplicationCard.tsx @@ -53,6 +53,10 @@ import { getCurrentUser } from "actions/authActions"; import Card, { ContextMenuTrigger } from "components/common/Card"; import { generateEditedByText } from "./helpers"; import { noop } from "lodash"; +import { getLatestGitBranchFromLocal } from "utils/storage"; +import { getCurrentUser as getCurrentUserSelector } from "selectors/usersSelectors"; +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; interface ApplicationCardProps { application: ApplicationPayload; @@ -98,6 +102,7 @@ export function ApplicationCard(props: ApplicationCardProps) { const theme = useContext(ThemeContext); const isSavingName = useSelector(getIsSavingAppName); const isErroredSavingName = useSelector(getIsErroredSavingAppName); + const currentUser = useSelector(getCurrentUserSelector); const initialsAndColorCode = getInitialsAndColorCode( props.application.name, theme.colors.appCardColors, @@ -116,7 +121,40 @@ export function ApplicationCard(props: ApplicationCardProps) { const dispatch = useDispatch(); const applicationId = props.application?.id; + const baseApplicationId = props.application?.baseId; const showGitBadge = props.application?.gitApplicationMetadata?.branchName; + const [editorParams, setEditorParams] = useState({}); + const isGitPersistBranchEnabled = useFeatureFlag( + FEATURE_FLAG.release_git_persist_branch_enabled, + ); + + useEffect(() => { + (async () => { + const storedLatestBranch = await getLatestGitBranchFromLocal( + currentUser?.email ?? "", + baseApplicationId, + ); + + if (isGitPersistBranchEnabled && storedLatestBranch) { + setEditorParams({ branch: storedLatestBranch }); + } else if (showGitBadge) { + setEditorParams({ branch: showGitBadge }); + } + })(); + }, [ + baseApplicationId, + currentUser?.email, + showGitBadge, + isGitPersistBranchEnabled, + ]); + + const viewerParams = useMemo(() => { + if (showGitBadge) { + return { branch: showGitBadge }; + } else { + return {}; + } + }, [showGitBadge]); useEffect(() => { let colorCode; @@ -273,15 +311,6 @@ export function ApplicationCard(props: ApplicationCardProps) { initials += props.application.name[1].toUpperCase() || ""; } - // should show correct branch of application when edit mode - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params: any = {}; - - if (showGitBadge) { - params.branch = showGitBadge; - } - const handleMenuOnClose = (open: boolean) => { if (!open && !isDeleting) { setIsMenuOpen(false); @@ -437,16 +466,16 @@ export function ApplicationCard(props: ApplicationCardProps) { if (!basePageId) return ""; - return builderURL({ basePageId, params }); - }, [props.application.defaultBasePageId, params]); + return builderURL({ basePageId, params: editorParams }); + }, [props.application.defaultBasePageId, editorParams]); const viewModeURL = useMemo(() => { const basePageId = props.application.defaultBasePageId; if (!basePageId) return ""; - return viewerURL({ basePageId, params }); - }, [props.application.defaultBasePageId, params]); + return viewerURL({ basePageId, params: viewerParams }); + }, [props.application.defaultBasePageId, viewerParams]); const launchApp = useCallback(() => { setURLParams(); @@ -463,11 +492,11 @@ export function ApplicationCard(props: ApplicationCardProps) { history.push( viewerURL({ basePageId: props.application.defaultBasePageId, - params, + params: viewerParams, }), ); dispatch(getCurrentUser()); - }, [props.application.defaultPageId]); + }, [dispatch, props.application.defaultBasePageId, viewerParams]); return ( state.ui.gitSync; @@ -280,3 +281,8 @@ export const isGitSettingsModalOpenSelector = (state: AppState) => export const activeGitSettingsModalTabSelector = (state: AppState) => state.ui.gitSync.activeGitSettingsModalTab; + +export const isGitPersistBranchEnabledSelector = createSelector( + selectFeatureFlags, + (featureFlags) => featureFlags.release_git_persist_branch_enabled ?? false, +); diff --git a/app/client/src/utils/storage.ts b/app/client/src/utils/storage.ts index 1282eded365d..dff7c051aaf3 100644 --- a/app/client/src/utils/storage.ts +++ b/app/client/src/utils/storage.ts @@ -43,6 +43,7 @@ export const STORAGE_KEYS: { CODE_WIDGET_NAVIGATION_USED: "CODE_WIDGET_NAVIGATION_USED", OVERRIDDEN_FEATURE_FLAGS: "OVERRIDDEN_FEATURE_FLAGS", ACTION_TEST_PAYLOAD: "ACTION_TEST_PAYLOAD", + LATEST_GIT_BRANCH: "LATEST_GIT_BRANCH", }; const store = localforage.createInstance({ @@ -1087,3 +1088,52 @@ export const storeActionTestPayload = async (payload: { return false; } }; + +export const setLatestGitBranchInLocal = async ( + userEmail: string, + baseApplicationId: string, + branch: string, +) => { + try { + const storedBranches: Record< + string, + Record + > = (await store.getItem(STORAGE_KEYS.LATEST_GIT_BRANCH)) ?? {}; + const userBranches = storedBranches?.[userEmail] ?? {}; + const newBranches = { + ...(storedBranches ?? {}), + [userEmail]: { + ...userBranches, + [baseApplicationId]: branch, + }, + }; + + await store.setItem(STORAGE_KEYS.LATEST_GIT_BRANCH, newBranches); + + return true; + } catch (error) { + log.error("An error occurred while setting LATEST_GIT_BRANCH"); + log.error(error); + + return false; + } +}; + +export const getLatestGitBranchFromLocal = async ( + userEmail: string, + baseApplicationId: string, +) => { + try { + const storedBranches: Record> | null = + await store.getItem(STORAGE_KEYS.LATEST_GIT_BRANCH); + const userBranches = storedBranches?.[userEmail] ?? {}; + const branch = userBranches?.[baseApplicationId] ?? null; + + return branch; + } catch (error) { + log.error("An error occurred while fetching LATEST_GIT_BRANCH"); + log.error(error); + + return null; + } +};