diff --git a/packages/app/src/app/graphql/types.ts b/packages/app/src/app/graphql/types.ts
index b2600df0112..1defc80aca0 100644
--- a/packages/app/src/app/graphql/types.ts
+++ b/packages/app/src/app/graphql/types.ts
@@ -505,6 +505,7 @@ export enum Direction {
export type TeamFeatureFlags = {
__typename?: 'TeamFeatureFlags';
blockRepoImport: Scalars['Boolean'];
+ blockBranchCreation: Scalars['Boolean'];
friendOfCsb: Scalars['Boolean'];
ubbBeta: Scalars['Boolean'];
};
@@ -3573,6 +3574,7 @@ export type TeamFragmentDashboardFragment = {
featureFlags: {
__typename?: 'TeamFeatureFlags';
blockRepoImport: boolean;
+ blockBranchCreation: boolean;
ubbBeta: boolean;
friendOfCsb: boolean;
};
@@ -3696,6 +3698,7 @@ export type CurrentTeamInfoFragmentFragment = {
featureFlags: {
__typename?: 'TeamFeatureFlags';
blockRepoImport: boolean;
+ blockBranchCreation: boolean;
ubbBeta: boolean;
friendOfCsb: boolean;
};
@@ -3870,6 +3873,7 @@ export type _CreateTeamMutation = {
featureFlags: {
__typename?: 'TeamFeatureFlags';
blockRepoImport: boolean;
+ blockBranchCreation: boolean;
ubbBeta: boolean;
friendOfCsb: boolean;
};
@@ -4728,6 +4732,7 @@ export type AllTeamsQuery = {
featureFlags: {
__typename?: 'TeamFeatureFlags';
blockRepoImport: boolean;
+ blockBranchCreation: boolean;
ubbBeta: boolean;
friendOfCsb: boolean;
};
@@ -5178,6 +5183,7 @@ export type GetTeamQuery = {
featureFlags: {
__typename?: 'TeamFeatureFlags';
blockRepoImport: boolean;
+ blockBranchCreation: boolean;
ubbBeta: boolean;
friendOfCsb: boolean;
};
diff --git a/packages/app/src/app/hooks/useWorkspaceFeatureFlags.ts b/packages/app/src/app/hooks/useWorkspaceFeatureFlags.ts
index 1fb9cf49288..119ad7f2ce2 100644
--- a/packages/app/src/app/hooks/useWorkspaceFeatureFlags.ts
+++ b/packages/app/src/app/hooks/useWorkspaceFeatureFlags.ts
@@ -2,6 +2,7 @@ import { useAppState } from 'app/overmind';
export type FeatureFlags = {
blockRepoImport: boolean;
+ blockBranchCreation: boolean;
ubbBeta: boolean;
friendOfCsb: boolean;
};
@@ -12,6 +13,7 @@ export const useWorkspaceFeatureFlags = (): FeatureFlags => {
if (!activeTeamInfo) {
return {
blockRepoImport: false,
+ blockBranchCreation: false,
ubbBeta: false,
friendOfCsb: false,
};
@@ -19,6 +21,7 @@ export const useWorkspaceFeatureFlags = (): FeatureFlags => {
return {
blockRepoImport: activeTeamInfo.featureFlags.blockRepoImport,
+ blockBranchCreation: activeTeamInfo.featureFlags.blockBranchCreation,
ubbBeta: activeTeamInfo.featureFlags.ubbBeta,
friendOfCsb: activeTeamInfo.featureFlags.friendOfCsb,
};
diff --git a/packages/app/src/app/overmind/actions.ts b/packages/app/src/app/overmind/actions.ts
index ceb65ab0e5e..58af0a64e15 100755
--- a/packages/app/src/app/overmind/actions.ts
+++ b/packages/app/src/app/overmind/actions.ts
@@ -159,7 +159,8 @@ type ModalName =
| 'sandboxPicker'
| 'minimumPrivacy'
| 'import'
- | 'create';
+ | 'create'
+ | 'branchCreationDeprecated';
export const modalOpened = (
{ state, effects }: Context,
diff --git a/packages/app/src/app/overmind/effects/gql/dashboard/fragments.ts b/packages/app/src/app/overmind/effects/gql/dashboard/fragments.ts
index c9d4e35df95..f3b68b6c124 100644
--- a/packages/app/src/app/overmind/effects/gql/dashboard/fragments.ts
+++ b/packages/app/src/app/overmind/effects/gql/dashboard/fragments.ts
@@ -161,6 +161,7 @@ export const teamFragmentDashboard = gql`
featureFlags {
blockRepoImport
+ blockBranchCreation
ubbBeta
friendOfCsb
}
@@ -279,6 +280,7 @@ export const currentTeamInfoFragment = gql`
featureFlags {
blockRepoImport
+ blockBranchCreation
ubbBeta
friendOfCsb
}
diff --git a/packages/app/src/app/overmind/namespaces/dashboard/actions.ts b/packages/app/src/app/overmind/namespaces/dashboard/actions.ts
index 28c70745c5f..97dcaa3ccff 100755
--- a/packages/app/src/app/overmind/namespaces/dashboard/actions.ts
+++ b/packages/app/src/app/overmind/namespaces/dashboard/actions.ts
@@ -1755,7 +1755,7 @@ export const forkGitHubRepository = async (
};
export const createDraftBranch = async (
- { state, effects }: Context,
+ { state, actions, effects }: Context,
{
owner,
name,
@@ -1767,6 +1767,11 @@ export const createDraftBranch = async (
return;
}
+ if (state.activeTeamInfo?.featureFlags.blockBranchCreation) {
+ actions.modalOpened({ modal: 'branchCreationDeprecated' });
+ return;
+ }
+
try {
state.dashboard.creatingBranch = true;
@@ -1793,8 +1798,23 @@ export const createDraftBranch = async (
}
} catch (error) {
state.dashboard.creatingBranch = false;
+
+ // The server blocks branch creation while the product is being
+ // deprecated. Surface the same deprecation modal instead of a
+ // generic error toast.
+ if (
+ error.response?.errors?.[0]?.extensions?.code ===
+ 'BRANCH_CREATION_DISABLED'
+ ) {
+ actions.modalOpened({ modal: 'branchCreationDeprecated' });
+ return;
+ }
+
notificationState.addNotification({
- message: JSON.stringify(error),
+ message:
+ error.response?.errors?.[0]?.message ||
+ error.message ||
+ 'Something went wrong while creating the branch.',
title: 'Failed to create branch',
status: NotificationStatus.ERROR,
});
diff --git a/packages/app/src/app/pages/Dashboard/Components/shared/RepositoryDeprecationStripe.tsx b/packages/app/src/app/pages/Dashboard/Components/shared/RepositoryDeprecationStripe.tsx
new file mode 100644
index 00000000000..36cd39ff3b6
--- /dev/null
+++ b/packages/app/src/app/pages/Dashboard/Components/shared/RepositoryDeprecationStripe.tsx
@@ -0,0 +1,20 @@
+import { MessageStripe } from '@codesandbox/components';
+import React from 'react';
+
+export const RepositoryDeprecationStripe: React.FC = () => {
+ return (
+
+ Branch creation is deprecated and the repositories product will be removed
+ on July 15th. Make sure to commit and push any work on your branches before
+ then.
+
+ Migration guide
+
+
+ );
+};
diff --git a/packages/app/src/app/pages/Dashboard/Content/routes/Repositories/index.tsx b/packages/app/src/app/pages/Dashboard/Content/routes/Repositories/index.tsx
index 405e05e99ce..c29ac84ee1a 100644
--- a/packages/app/src/app/pages/Dashboard/Content/routes/Repositories/index.tsx
+++ b/packages/app/src/app/pages/Dashboard/Content/routes/Repositories/index.tsx
@@ -11,6 +11,8 @@ import { RestrictedPublicReposImport } from 'app/pages/Dashboard/Components/shar
import { useDismissible } from 'app/hooks';
import track from '@codesandbox/common/lib/utils/analytics';
import { useWorkspaceLimits } from 'app/hooks/useWorkspaceLimits';
+import { useWorkspaceFeatureFlags } from 'app/hooks/useWorkspaceFeatureFlags';
+import { RepositoryDeprecationStripe } from 'app/pages/Dashboard/Components/shared/RepositoryDeprecationStripe';
import { EmptyRepositories } from './EmptyRepositories';
export const RepositoriesPage = () => {
@@ -23,6 +25,7 @@ export const RepositoriesPage = () => {
const [dismissedPermissionsBanner, dismissPermissionsBanner] = useDismissible(
'DASHBOARD_REPOSITORIES_PERMISSIONS_BANNER'
);
+ const { blockBranchCreation } = useWorkspaceFeatureFlags();
const teamRepos = repositoriesByTeamId[activeTeam] || undefined;
@@ -100,6 +103,12 @@ export const RepositoriesPage = () => {
title="All repositories"
/>
+ {blockBranchCreation && (
+
+
+
+ )}
+
{messageStripe && (
{messageStripe}
diff --git a/packages/app/src/app/pages/Dashboard/Content/routes/RepositoryBranches/index.tsx b/packages/app/src/app/pages/Dashboard/Content/routes/RepositoryBranches/index.tsx
index 6a3eb2d8e38..24a1be2c635 100644
--- a/packages/app/src/app/pages/Dashboard/Content/routes/RepositoryBranches/index.tsx
+++ b/packages/app/src/app/pages/Dashboard/Content/routes/RepositoryBranches/index.tsx
@@ -13,7 +13,9 @@ import {
} from 'app/overmind/namespaces/dashboard/utils';
import { BranchWithPrFragment } from 'app/graphql/types';
import { InstallGHAppStripe } from 'app/pages/Dashboard/Components/shared/InstallGHAppStripe';
+import { RepositoryDeprecationStripe } from 'app/pages/Dashboard/Components/shared/RepositoryDeprecationStripe';
import { useWorkspaceLimits } from 'app/hooks/useWorkspaceLimits';
+import { useWorkspaceFeatureFlags } from 'app/hooks/useWorkspaceFeatureFlags';
type MappedBranches = {
defaultBranch: BranchWithPrFragment | null;
@@ -24,6 +26,7 @@ type MappedBranches = {
export const RepositoryBranchesPage = () => {
const { isFrozen } = useWorkspaceLimits();
+ const { blockBranchCreation } = useWorkspaceFeatureFlags();
const params = useParams<{ path: string }>();
const path = params.path || '';
const [, owner, name] = path.split('/');
@@ -128,6 +131,12 @@ export const RepositoryBranchesPage = () => {
readOnly={isFrozen}
/>
+ {blockBranchCreation && (
+
+
+
+ )}
+
{repositoryProject?.appInstalled === false && (
{
+ const { modalClosed } = useActions();
+
+ return (
+
+ The repositories product will be removed on July 15th. Make sure to
+ commit and push any work on your branches before then.{' '}
+
+ Visit our documentation for migration options.
+
+ >
+ }
+ confirmMessage="Close"
+ type="secondary"
+ onPrimaryAction={modalClosed}
+ />
+ );
+};
diff --git a/packages/app/src/app/pages/common/Modals/index.tsx b/packages/app/src/app/pages/common/Modals/index.tsx
index 60d43eb233f..047c5613929 100644
--- a/packages/app/src/app/pages/common/Modals/index.tsx
+++ b/packages/app/src/app/pages/common/Modals/index.tsx
@@ -21,6 +21,7 @@ import { AccountDeletionModal } from './AccountDeletion';
import { AccountDeletionConfirmationModal } from './AccountDeletion/DeletedConfirmation';
import { UndoAccountDeletionModal } from './UndoAccountDeletion';
import { UndoAccountDeletionConfirmationModal } from './UndoAccountDeletion/UndoDeletedConfirmation';
+import { BranchCreationDeprecatedModal } from './BranchCreationDeprecatedModal';
const modals = {
preferences: {
@@ -92,6 +93,10 @@ const modals = {
Component: UndoAccountDeletionConfirmationModal,
width: 450,
},
+ branchCreationDeprecated: {
+ Component: BranchCreationDeprecatedModal,
+ width: 450,
+ },
};
const Modals: FunctionComponent = () => {