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 = () => {