diff --git a/app/src/components/Home/home.scss b/app/src/components/Home/home.scss index 48b3c1c9a1..20eab47748 100644 --- a/app/src/components/Home/home.scss +++ b/app/src/components/Home/home.scss @@ -30,14 +30,19 @@ & .homepage-primary-cards-wrapper { gap: 24px; - justify-content: center; + justify-content: flex-start; align-items: flex-start; margin-top: 40px; + margin-left: 90px; padding-bottom: 12px; & > .ant-col { max-width: 49%; } + + > div{ + width: 460px; + } } .ant-skeleton-paragraph li { @@ -48,7 +53,7 @@ .homepage-emptycard { width: 100%; min-width: 325px; - max-width: 325px; + max-width: 500px; padding-top: 20px; height: 304px; display: flex; diff --git a/app/src/componentsV2/modals/CreateWorkspaceModalOld/CreateWorkspaceModalOld.tsx b/app/src/componentsV2/modals/CreateWorkspaceModalOld/CreateWorkspaceModalOld.tsx index c2ec8a5be2..795e2afed7 100644 --- a/app/src/componentsV2/modals/CreateWorkspaceModalOld/CreateWorkspaceModalOld.tsx +++ b/app/src/componentsV2/modals/CreateWorkspaceModalOld/CreateWorkspaceModalOld.tsx @@ -1,14 +1,11 @@ import React, { useCallback, useState } from "react"; -import { Checkbox, Input, Modal, Radio, Tag } from "antd"; +import { Checkbox, Input, Modal, Radio } from "antd"; import { useDispatch, useSelector } from "react-redux"; import * as Sentry from "@sentry/react"; import { getAppMode } from "store/selectors"; import { RQButton } from "lib/design-system-v2/components"; -import { CreateTeamParams, LocalWorkspaceConfig, SharedOrPrivateWorkspaceConfig } from "types"; -import { displayFolderSelector } from "components/mode-specific/desktop/misc/FileDialogButton"; -import { CONSTANTS as GLOBAL_CONSTANTS } from "@requestly/requestly-core"; -import { IoMdClose } from "@react-icons/all-files/io/IoMdClose"; +import { CreateTeamParams, SharedOrPrivateWorkspaceConfig } from "types"; import { getFunctions, httpsCallable } from "firebase/functions"; import { trackAddTeamMemberSuccess, @@ -24,10 +21,8 @@ import { switchWorkspace } from "actions/TeamWorkspaceActions"; import { redirectToTeam } from "utils/RedirectionUtils"; import { useNavigate } from "react-router-dom"; import { getAvailableBillingTeams } from "store/features/billing/selectors"; -import { createWorkspaceFolder } from "services/fsManagerServiceAdapter"; import { isActiveWorkspaceShared } from "store/slices/workspaces/selectors"; -import { workspaceActions } from "store/slices/workspaces/slice"; -import { Workspace, WorkspaceMemberRole, WorkspaceType } from "features/workspaces/types"; +import { WorkspaceType } from "features/workspaces/types"; import "./createOldWorkspaceModal.scss"; interface Props { @@ -45,17 +40,10 @@ export const CreateWorkspaceModalOld: React.FC = ({ isOpen, defaultWorksp const isSharedWorkspaceMode = useSelector(isActiveWorkspaceShared); const billingTeams = useSelector(getAvailableBillingTeams); const [workspaceName, setWorkspaceName] = useState(""); - const [workspaceType, setWorkspaceType] = useState( - defaultWorkspaceType || (user.loggedIn ? WorkspaceType.SHARED : WorkspaceType.LOCAL) - ); - const [folderPath, setFolderPath] = useState(""); + const [workspaceType, setWorkspaceType] = useState(defaultWorkspaceType || WorkspaceType.SHARED); const [isLoading, setIsLoading] = useState(false); const [isNotifyAllSelected, setIsNotifyAllSelected] = useState(false); - const folderSelectCallback = (path: string) => { - setFolderPath(path); - }; - const handlePostTeamCreationStep = useCallback( (teamId: string, newTeamName: string, hasMembersInSameDomain: boolean) => { switchWorkspace( @@ -138,43 +126,15 @@ export const CreateWorkspaceModalOld: React.FC = ({ isOpen, defaultWorksp const createOrgTeamInvite = httpsCallable(functions, "invites-createOrganizationTeamInvite"); const upsertTeamCommonInvite = httpsCallable(functions, "invites-upsertTeamCommonInvite"); - const config = - workspaceType === WorkspaceType.LOCAL - ? ({ type: WorkspaceType.LOCAL, rootPath: folderPath } as LocalWorkspaceConfig) - : ({ type: WorkspaceType.SHARED } as SharedOrPrivateWorkspaceConfig); + const config = { type: WorkspaceType.SHARED } as SharedOrPrivateWorkspaceConfig; try { const teamId = await (async () => { - if (config.type === WorkspaceType.LOCAL) { - const workspaceCreationResult = await createWorkspaceFolder(workspaceName, config.rootPath); - if (workspaceCreationResult.type === "error") { - throw new Error(workspaceCreationResult.error.message); - } - const partialWorkspace = workspaceCreationResult.content; - const localWorkspace: Workspace = { - id: partialWorkspace.id, - name: partialWorkspace.name, - owner: user?.details?.profile?.uid, - accessCount: 1, - adminCount: 1, - members: { - [user?.details?.profile?.uid]: { - role: WorkspaceMemberRole.admin, - }, - }, - appsumo: null, - workspaceType: WorkspaceType.LOCAL, - rootPath: partialWorkspace.path, - }; - dispatch(workspaceActions.upsertWorkspace(localWorkspace)); - return partialWorkspace.id; - } else { - const response: any = await createTeam({ - teamName: workspaceName, - config, - }); - return response.data.teamId; - } + const response: any = await createTeam({ + teamName: workspaceName, + config, + }); + return response.data.teamId; })(); trackNewTeamCreateSuccess(teamId, workspaceName, "create_workspace_modal", workspaceType); @@ -216,7 +176,6 @@ export const CreateWorkspaceModalOld: React.FC = ({ isOpen, defaultWorksp }, [ billingTeams, callback, - dispatch, isNotifyAllSelected, toggleModal, user?.details?.profile?.email, @@ -224,8 +183,6 @@ export const CreateWorkspaceModalOld: React.FC = ({ isOpen, defaultWorksp handlePostTeamCreationStep, handleDomainInvitesCreation, workspaceType, - folderPath, - user?.details?.profile?.uid, ]); return ( @@ -245,7 +202,7 @@ export const CreateWorkspaceModalOld: React.FC = ({ isOpen, defaultWorksp Cancel @@ -300,45 +257,6 @@ export const CreateWorkspaceModalOld: React.FC = ({ isOpen, defaultWorksp ), }, - { - value: WorkspaceType.LOCAL, - disabled: appMode !== GLOBAL_CONSTANTS.APP_MODES.DESKTOP, - label: ( -
-
- Local workspace BETA -
-
- In a Local Workspace, your files are stored on your device. Currently, only API client files are - supported. -
- {workspaceType === WorkspaceType.LOCAL ? ( -
- {folderPath.length ? ( -
-
- SELECTED LOCATION setFolderPath("")} /> -
-
{folderPath}
-
- ) : ( - <> - displayFolderSelector(folderSelectCallback)} - > - Select a folder - -
A location to store your workspace data.
- - )} -
- ) : null} -
- ), - }, ]} > diff --git a/app/src/features/apiClient/errors/MigrationBlockedError/MigrationBlockedError.ts b/app/src/features/apiClient/errors/MigrationBlockedError/MigrationBlockedError.ts new file mode 100644 index 0000000000..80b9072eed --- /dev/null +++ b/app/src/features/apiClient/errors/MigrationBlockedError/MigrationBlockedError.ts @@ -0,0 +1,13 @@ +import { NativeError } from "errors/NativeError"; + +// Thrown by makeRequest when the migration block is active (RQ-4699). The +// legacy API Client stays mounted but request execution is stopped at the +// single choke point so every outbound path (HTTP/GraphQL send, response-panel +// Retry, Collection Runner, GraphQL introspection, WSDL/SOAP import fetch) is +// blocked. Pairs with the mandatory migration modal. +export class MigrationBlockedError extends NativeError { + constructor() { + super("API Client has moved to a dedicated app. Requests are disabled here."); + this.name = "MigrationBlockedError"; + } +} diff --git a/app/src/features/apiClient/screens/apiClient/utils.ts b/app/src/features/apiClient/screens/apiClient/utils.ts index 7dc9e12873..71323abc20 100644 --- a/app/src/features/apiClient/screens/apiClient/utils.ts +++ b/app/src/features/apiClient/screens/apiClient/utils.ts @@ -24,6 +24,9 @@ import { Request as HarRequest } from "har-format"; import { getDefaultAuth } from "./components/views/components/request/components/AuthorizationView/defaults"; import { ApiClientRecordsInterface } from "features/apiClient/helpers/modules/sync/interfaces"; import { UserAbortError } from "features/apiClient/errors/UserAbortError/UserAbortError"; +import { MigrationBlockedError } from "features/apiClient/errors/MigrationBlockedError/MigrationBlockedError"; +import { growthbook } from "utils/feature-flag/growthbook"; +import { MIGRATION_BLOCK_FLAG } from "features/apiClient/screens/migrationBlock/constants"; import { Authorization } from "./components/views/components/request/components/AuthorizationView/types/AuthConfig"; import { inheritAuthFromParent } from "features/apiClient/helpers/auth"; import { AutogeneratedFieldsStore, parseAuth, parseContentType } from "features/apiClient/store/autogenerateStore"; @@ -52,6 +55,17 @@ export const makeRequest = async ( request: RQAPI.HttpRequest, signal?: AbortSignal ): Promise => { + // RQ-4699: universal choke point. Every outbound path (HTTP/GraphQL send, + // response-panel Retry, Collection Runner, GraphQL introspection, WSDL/SOAP + // import fetch) funnels through makeRequest, so a single guard here is a full + // stop. Read the block state from the GrowthBook singleton (not a hook — this + // isn't a React context) so the guard can't itself throw; `isOn` returns a + // boolean even before features load. This mirrors the container's master + // kill-switch (MIGRATION_BLOCK_FLAG) that mounts the mandatory migration modal. + if (growthbook.isOn(MIGRATION_BLOCK_FLAG)) { + throw new MigrationBlockedError(); + } + return new Promise((resolve, reject) => { const abortListener = () => { if (signal) { diff --git a/app/src/features/apiClient/screens/migrationBlock/MigrationBlockModal.tsx b/app/src/features/apiClient/screens/migrationBlock/MigrationBlockModal.tsx index 50d6dc12e5..0d74aa3b7d 100644 --- a/app/src/features/apiClient/screens/migrationBlock/MigrationBlockModal.tsx +++ b/app/src/features/apiClient/screens/migrationBlock/MigrationBlockModal.tsx @@ -21,11 +21,7 @@ import { DOWNLOAD_URLS, DOWNLOAD_LABELS, REPORT_ISSUES_URL, - MIGRATION_BLOCK_DISMISSABLE_FLAG, MIGRATION_BLOCK_CLOUD_FORCE_FLAG, - MIGRATION_BLOCK_LOCAL_STORAGE_FLAG, - MIGRATION_BLOCK_CLOUD_FLAG, - MIGRATION_BLOCK_LOCAL_FS_FLAG, } from "./constants"; import { trackMigrationBlockScreenShown, @@ -80,14 +76,15 @@ function openExternalLink(url: string): void { export const MigrationBlockModal: React.FC = () => { const segment = useMigrationSegment(); const selectedWorkspaces = useGetAllSelectedWorkspaces(); - const dismissable = useFeatureIsOn(MIGRATION_BLOCK_DISMISSABLE_FLAG); + // RQ-4699: the migration block is mandatory. The modal is non-dismissable and + // renders for every segment — the per-segment enable flags are no longer + // consulted here. The only gate is the container's master kill-switch + // (MIGRATION_BLOCK_FLAG), which is also what the makeRequest guard reads, so + // the modal and the request block turn on together. + const dismissable = false; const forceCloudMigration = useFeatureIsOn(MIGRATION_BLOCK_CLOUD_FORCE_FLAG); - const isLocalStorageVariantOn = useFeatureIsOn(MIGRATION_BLOCK_LOCAL_STORAGE_FLAG); - const isCloudVariantOn = useFeatureIsOn(MIGRATION_BLOCK_CLOUD_FLAG); - const isLocalFsVariantOn = useFeatureIsOn(MIGRATION_BLOCK_LOCAL_FS_FLAG); if (segment === "local-storage") { - if (!isLocalStorageVariantOn) return null; // local-storage is always SINGLE view with exactly one selected workspace. const workspace = selectedWorkspaces[0]; if (!workspace || workspace.status.loading) return null; @@ -99,7 +96,6 @@ export const MigrationBlockModal: React.FC = () => { } if (segment === "auto-cloud") { - if (!isCloudVariantOn) return null; const workspace = selectedWorkspaces[0]; if (!workspace || workspace.status.loading) return null; return ( @@ -110,7 +106,6 @@ export const MigrationBlockModal: React.FC = () => { } if (segment === "auto-local-fs") { - if (!isLocalFsVariantOn) return null; // LocalFS data lives on disk and the new app reads the same paths, so the // copy is workspace-agnostic — no sign-in, no WorkspaceProvider at this // level. Same modal renders for SINGLE (one LOCAL ws) and MULTI (N LOCAL diff --git a/app/src/features/onboarding/components/recommendations/components/recommendations/personaRecommendationData.tsx b/app/src/features/onboarding/components/recommendations/components/recommendations/personaRecommendationData.tsx index 20dd8fa6ad..eb6cd7cb14 100644 --- a/app/src/features/onboarding/components/recommendations/components/recommendations/personaRecommendationData.tsx +++ b/app/src/features/onboarding/components/recommendations/components/recommendations/personaRecommendationData.tsx @@ -5,7 +5,6 @@ import { FeatureSection, FeatureReleaseTag } from "./types"; import { CONSTANTS as GLOBAL_CONSTANTS } from "@requestly/requestly-core"; import { FiVideo } from "@react-icons/all-files/fi/FiVideo"; import { IoDocumentTextOutline } from "@react-icons/all-files/io5/IoDocumentTextOutline"; -import { ApiOutlined } from "@ant-design/icons"; const { RULE_TYPES } = GLOBAL_CONSTANTS; const { RULE_TYPES_CONFIG } = APP_CONSTANTS; @@ -76,13 +75,6 @@ export const personaRecommendationData: FeatureSection[] = [ subTitle: "Create mocks for your APIs", link: PATHS.MOCK_SERVER_V2.ABSOLUTE, }, - { - id: "test_api_endpoints", - title: "Test API endpoints", - icon: () => , - subTitle: "Test responses quickly using API Client", - link: PATHS.API_CLIENT.ABSOLUTE, - }, ], }, ]; diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/DesktopOnboardingModal.tsx b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/DesktopOnboardingModal.tsx index a1e59e013b..8471370358 100644 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/DesktopOnboardingModal.tsx +++ b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/DesktopOnboardingModal.tsx @@ -1,19 +1,12 @@ import React, { useEffect, useState } from "react"; import { Button, Modal } from "antd"; -import { useNavigate } from "react-router-dom"; -import { WorkspaceCreationView } from "componentsV2/modals/CreateWorkspaceModal/components/WorkspaceCreationView"; import { AuthCard } from "./components/AuthCard/AuthCard"; -import { WelcomeCard } from "./components/WelcomeCard/WelcomeCard"; import { useDispatch, useSelector } from "react-redux"; import { globalActions } from "store/slices/global/slice"; import { getUserAuthDetails } from "store/slices/global/user/selectors"; -// import { IoMdArrowBack } from "@react-icons/all-files/io/IoMdArrowBack"; -import { redirectToApiClient } from "utils/RedirectionUtils"; import "./desktopOnboardingModal.scss"; import { trackDesktopOnboardingStepSkipped, trackDesktopOnboardingViewed } from "./analytics"; import { OnboardingStep } from "./types"; -import { WorkspaceType } from "features/workspaces/types"; -import { WorkspaceCreationProvider } from "componentsV2/modals/CreateWorkspaceModal/context"; export const DesktopOnboardingCard = ({ children, className }: { children: React.ReactNode; className?: string }) => { return
{children}
; @@ -21,9 +14,11 @@ export const DesktopOnboardingCard = ({ children, className }: { children: React export const DesktopOnboardingModal = () => { const dispatch = useDispatch(); - const navigate = useNavigate(); const user = useSelector(getUserAuthDetails); - const [onboardingStep, setOnboardingStep] = useState(OnboardingStep.FEATURE_SELECTION); + // First-run onboarding is now a single step: sign-in/sign-up. The feature-selection + // screen (WelcomeCard) and the local-workspace folder-selection step were removed + // with the API Client (RQ-4695), so the step machine starts — and stays — at AUTH. + const [onboardingStep] = useState(OnboardingStep.AUTH); useEffect(() => { if (user.loggedIn) { @@ -42,43 +37,21 @@ export const DesktopOnboardingModal = () => { wrapClassName="rq-desktop-onboarding-modal-wrapper" className="rq-desktop-onboarding-modal" > - -
- {onboardingStep === OnboardingStep.FEATURE_SELECTION ? ( - - setOnboardingStep(step)} /> - - ) : onboardingStep === OnboardingStep.FOLDER_SELECTION ? ( - - setOnboardingStep(OnboardingStep.FEATURE_SELECTION)} - callback={() => { - dispatch(globalActions.updateIsOnboardingCompleted(true)); - redirectToApiClient(navigate); - }} - analyticEventSource="desktop_onboarding_modal" - /> - - ) : onboardingStep === OnboardingStep.AUTH ? ( - <> - - setOnboardingStep(OnboardingStep.FEATURE_SELECTION)} /> - - - - ) : null} -
-
+
+ + + + +
); }; diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/AuthCard/AuthCard.tsx b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/AuthCard/AuthCard.tsx index 48bc6f4a71..e8079e9667 100644 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/AuthCard/AuthCard.tsx +++ b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/AuthCard/AuthCard.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { IoMdArrowBack } from "@react-icons/all-files/io/IoMdArrowBack"; import { RQButton } from "lib/design-system-v2/components"; import LINKS from "config/constants/sub/links"; import { useDispatch, useSelector } from "react-redux"; @@ -10,11 +9,7 @@ import { SOURCE } from "modules/analytics/events/common/constants"; import APP_CONSTANTS from "config/constants"; import "./authCard.scss"; -interface Props { - onBackClick: () => void; -} - -export const AuthCard: React.FC = ({ onBackClick }) => { +export const AuthCard: React.FC = () => { const dispatch = useDispatch(); const appMode = useSelector(getAppMode); @@ -33,10 +28,7 @@ export const AuthCard: React.FC = ({ onBackClick }) => { }; return ( <> -
- - Create your free account -
+
Create your free account
You will be redirected to your browser to securely complete the sign-up process in a few simple steps. diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/WelcomeCard.tsx b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/WelcomeCard.tsx deleted file mode 100644 index ea8b6671ca..0000000000 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/WelcomeCard.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Button } from "antd"; -import RQOnlyLogo from "/assets/media/common/RQ-only-logo.svg"; -import { useDispatch } from "react-redux"; -import { globalActions } from "store/slices/global/slice"; -import { trackDesktopOnboardingFeatureSelected, trackDesktopOnboardingStepSkipped } from "../../analytics"; -import { OnboardingStep } from "../../types"; -import { WelcomeCardHeader } from "./components/WelcomeCardHeader/WelcomeCardHeader"; -import { AnimatePresence, m } from "framer-motion"; -import { collapseExpandTransition } from "utils/animations"; -import { MdOutlineKeyboardArrowUp } from "@react-icons/all-files/md/MdOutlineKeyboardArrowUp"; -import { MdOutlineKeyboardArrowDown } from "@react-icons/all-files/md/MdOutlineKeyboardArrowDown"; -import { LocalWorkspaceCreateOptions } from "componentsV2/modals/CreateWorkspaceModal"; -import clsx from "clsx"; -import { useCreateDefaultLocalWorkspace } from "features/workspaces/hooks/useCreateDefaultLocalWorkspace"; -import { FileSystemError } from "features/apiClient/helpers/modules/sync/local/services/types"; -import { OpenWorkspaceErrorView } from "componentsV2/modals/CreateWorkspaceModal/components/LocalWorkspaceCreationView/components/OpenWorkspaceErrorView"; -import { useOpenLocalWorkspace } from "features/workspaces/hooks/useOpenLocalWorkspace"; -import { MdOutlineKeyboardArrowRight } from "@react-icons/all-files/md/MdOutlineKeyboardArrowRight"; -import "./welcomeCard.scss"; -import { useNavigate } from "react-router-dom"; -import { redirectToApiClient } from "utils/RedirectionUtils"; - -interface Props { - onFeatureClick: (step: OnboardingStep) => void; -} - -export const WelcomeCard: React.FC = ({ onFeatureClick }) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const [isApiClientCardExpanded, setIsApiClientCardExpanded] = useState(false); - const [openWorkspaceError, setOpenWorkspaceError] = useState(null); - const apiClientCardRef = useRef(null); - - const { createWorkspace, isLoading } = useCreateDefaultLocalWorkspace({ - analyticEventSource: "desktop_onboarding_modal", - onCreateWorkspaceCallback: () => { - trackDesktopOnboardingStepSkipped(OnboardingStep.FEATURE_SELECTION); - dispatch(globalActions.updateIsOnboardingCompleted(true)); - redirectToApiClient(navigate); - }, - }); - - const { openWorkspace, isLoading: isOpeningWorkspaceLoading } = useOpenLocalWorkspace({ - analyticEventSource: "desktop_onboarding_modal", - onOpenWorkspaceCallback: () => { - dispatch(globalActions.updateIsOnboardingCompleted(true)); - redirectToApiClient(navigate); - }, - onError: (error: FileSystemError) => { - setOpenWorkspaceError(error); - }, - }); - - useEffect(() => { - if (!isApiClientCardExpanded) { - return; - } - - const handleClickOutside = (event: MouseEvent) => { - if (apiClientCardRef.current && !apiClientCardRef.current.contains(event.target as Node)) { - setIsApiClientCardExpanded(false); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [isApiClientCardExpanded]); - - const handleApiClientCardExpandToggle = () => { - if (!isApiClientCardExpanded) { - trackDesktopOnboardingFeatureSelected("api_client"); - } - setIsApiClientCardExpanded((prev) => !prev); - }; - - if (openWorkspaceError) { - return ( -
- onFeatureClick(OnboardingStep.FOLDER_SELECTION)} - openWorkspace={openWorkspace} - isOpeningWorkspaceLoading={isOpeningWorkspaceLoading} - analyticEventSource="desktop_onboarding_modal" - /> -
- ); - } - - return ( -
- RQ Only Logo -
- Welcome to Requestly -
-
- Intercept, Mock, Build & Test APIs faster -
-
- -
-
- - {isApiClientCardExpanded ? ( - - ) : ( - - )} -
- - {isApiClientCardExpanded && ( - -
- onFeatureClick(OnboardingStep.FOLDER_SELECTION)} - onCreationCallback={() => { - dispatch(globalActions.updateIsOnboardingCompleted(true)); - redirectToApiClient(navigate); - }} - openWorkspace={openWorkspace} - isOpeningWorkspaceLoading={isOpeningWorkspaceLoading} - /> -
-
- )} -
-
-
-
{ - trackDesktopOnboardingFeatureSelected("rules"); - onFeatureClick(OnboardingStep.AUTH); - }} - > - - -
-
-
- {" "} - this screen and quick start -
-
- ); -}; diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/components/WelcomeCardHeader/WelcomeCardHeader.tsx b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/components/WelcomeCardHeader/WelcomeCardHeader.tsx deleted file mode 100644 index ee61b71cf7..0000000000 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/components/WelcomeCardHeader/WelcomeCardHeader.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from "react"; -import "./welcomeCardHeader.scss"; -import { m, AnimatePresence } from "framer-motion"; -import { collapseExpandTransition } from "utils/animations"; - -interface WelcomeCardHeaderProps { - title: string; - description: string | null; - iconSrc: string; -} -export const WelcomeCardHeader: React.FC = ({ - title, - description, - iconSrc, -}: WelcomeCardHeaderProps) => { - return ( -
-
- {title} -
-
-
{title}
- - {description && ( - -
{description}
-
- )} -
-
-
- ); -}; diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/components/WelcomeCardHeader/welcomeCardHeader.scss b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/components/WelcomeCardHeader/welcomeCardHeader.scss deleted file mode 100644 index da7fe40998..0000000000 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/components/WelcomeCardHeader/welcomeCardHeader.scss +++ /dev/null @@ -1,40 +0,0 @@ -.welcome-card-header { - display: flex; - align-items: center; - gap: 12px; - - &__icon { - display: flex; - width: 48px; - height: 48px; - aspect-ratio: 1/1; - justify-content: center; - align-items: center; - gap: 8px; - border-radius: 50%; - border: 1px solid var(--stroke-glass, rgba(255, 255, 255, 0.4)); - background: rgba(255, 255, 255, 0.04); - - img { - width: 29px; - height: 29px; - flex-shrink: 0; - filter: brightness(0.7); - } - } - - &__content { - &-title { - color: var(--requestly-color-text-default); - font-size: var(--requestly-font-size-md); - font-weight: var(--requestly-font-weight-medium); - } - - &-description { - color: var(--requestly-color-text-subtle); - font-size: var(--requestly-font-size-sm); - margin-top: 4px; - width: 260px; - } - } -} diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/welcomeCard.scss b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/welcomeCard.scss deleted file mode 100644 index e35625e196..0000000000 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/components/WelcomeCard/welcomeCard.scss +++ /dev/null @@ -1,56 +0,0 @@ -.welcome-card-option { - background: rgba(255, 255, 255, 0.04); - border-radius: 12px; - backdrop-filter: blur(10px); - cursor: pointer; - padding: var(--requestly-space-6) var(--requestly-space-8); - transition: border 0.2s ease-in-out; - border: 1px solid transparent; - - &:hover { - border: 1px solid var(--requestly-color-primary); - background: var(--requestly-color-white-t-10); - } -} - -.fade-out { - opacity: 0.2; - transition: opacity 0.2s ease-in-out; - pointer-events: none; -} - -.api-client-onboard-card { - position: relative; - - &__options-container { - padding-top: 12px; - } - - &__arrow { - width: 20px; - height: 20px; - flex-shrink: 0; - position: absolute; - right: 16px; - top: 32px; - } -} - -.api-client-onboard-card__options-container { - .local-workspace-create-options__option-title { - font-size: var(--requestly-font-size-md) !important; - } -} - -.onboarding-interceptor-card-option { - .api-client-onboard-card__arrow { - top: 35px; - opacity: 0; - } - - &:hover { - .api-client-onboard-card__arrow { - opacity: 1; - } - } -} diff --git a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/types.ts b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/types.ts index 271a41f8d8..13de4238b1 100644 --- a/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/types.ts +++ b/app/src/features/onboarding/componentsV2/DesktopOnboardingModal/types.ts @@ -1,5 +1,3 @@ export enum OnboardingStep { - FEATURE_SELECTION = "feature_selection", - FOLDER_SELECTION = "folder_selection", AUTH = "auth", } diff --git a/app/src/features/workspaces/hooks/useWorkspaceFetcher.ts b/app/src/features/workspaces/hooks/useWorkspaceFetcher.ts index 53a1580f9a..181764c213 100644 --- a/app/src/features/workspaces/hooks/useWorkspaceFetcher.ts +++ b/app/src/features/workspaces/hooks/useWorkspaceFetcher.ts @@ -1,5 +1,4 @@ import { useEffect } from "react"; -import { useFetchLocalWorkspaces } from "./useFetchLocalWorkspaces"; import { useFetchTeamWorkspaces } from "./useFetchTeamWorkspaces"; import { useDispatch, useSelector } from "react-redux"; import { workspaceActions } from "store/slices/workspaces/slice"; @@ -17,20 +16,21 @@ export const useWorkspaceFetcher = () => { const activeWorkspace = useSelector(getActiveWorkspace); const appMode = useSelector(getAppMode); - const { fetchLocalWorkspaces } = useFetchLocalWorkspaces(); const { sharedWorkspaces } = useFetchTeamWorkspaces(); useActiveWorkspacesMembersListener(); useEffect(() => { - (async () => { - try { - const allLocalWorkspaces = await fetchLocalWorkspaces(); - dispatch(workspaceActions.setAllWorkspaces([...allLocalWorkspaces, ...sharedWorkspaces])); - } catch (e) { - captureException(e); - } - })(); - }, [dispatch, fetchLocalWorkspaces, sharedWorkspaces]); + // LOCAL (file-system) workspaces are hidden from the UI (RQ-4696): they only + // ever backed the API Client, which has been removed. We no longer fetch or + // inject them, so nothing that reads `allWorkspaces` (dropdown, overlay, + // settings) surfaces a LOCAL workspace. On-disk data is untouched, and + // LOCAL_STORAGE (logged-out) is unaffected — it flows through its own path. + try { + dispatch(workspaceActions.setAllWorkspaces([...sharedWorkspaces])); + } catch (e) { + captureException(e); + } + }, [dispatch, sharedWorkspaces]); useEffect(() => { if (!user.loggedIn) { diff --git a/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspaceDropdown.tsx b/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspaceDropdown.tsx index 23aef14eff..502ff36ee7 100644 --- a/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspaceDropdown.tsx +++ b/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspaceDropdown.tsx @@ -1,22 +1,16 @@ -import React, { useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { useSelector } from "react-redux"; import { DownOutlined } from "@ant-design/icons"; -import { MdOutlineRefresh } from "@react-icons/all-files/md/MdOutlineRefresh"; import { getAppMode } from "store/selectors"; import { Dropdown, Tooltip } from "antd"; -import FEATURES from "config/constants/sub/features"; import WorkspaceAvatar from "features/workspaces/components/WorkspaceAvatar"; -import { RQButton } from "lib/design-system-v2/components"; import { trackTopbarClicked } from "modules/analytics/events/common/onboarding/header"; import { getActiveWorkspace } from "store/slices/workspaces/selectors"; import { Invite } from "types"; -import { isFeatureCompatible } from "utils/CompatibilityUtils"; import { WorkspacesOverlay } from "./WorkspacesOverlay/WorkspacesOverlay"; import { MultiWorkspaceAvatarGroup } from "../MultiWorkspaceAvatarGroup"; -import LocalWorkspaceAvatar from "features/workspaces/components/LocalWorkspaceAvatar"; import { CONSTANTS as GLOBAL_CONSTANTS } from "@requestly/requestly-core"; import { getUserAuthDetails } from "store/slices/global/user/selectors"; -import { WorkspaceType } from "features/workspaces/types"; import { ApiClientViewMode, useViewMode } from "features/apiClient/slices"; const prettifyWorkspaceName = (workspaceName: string) => { @@ -50,23 +44,12 @@ const WorkSpaceDropDown = ({ teamInvites }: { teamInvites: Invite[] }) => { } }; - const handleLocalSyncRefresh = (e: React.MouseEvent) => { - e.stopPropagation(); - window.dispatchEvent(new Event("local-sync-refresh")); - }; - const toggleDropdown = () => { setIsDropdownOpen((prev) => !prev); }; const tooltipTitle = - activeWorkspace?.workspaceType === WorkspaceType.LOCAL - ? viewMode === ApiClientViewMode.SINGLE - ? activeWorkspace.rootPath - : null - : viewMode === ApiClientViewMode.MULTI - ? null - : prettifyWorkspaceName(activeWorkspaceName); + viewMode === ApiClientViewMode.MULTI ? null : prettifyWorkspaceName(activeWorkspaceName); return ( <> @@ -94,29 +77,14 @@ const WorkSpaceDropDown = ({ teamInvites }: { teamInvites: Invite[] }) => { {viewMode === ApiClientViewMode.MULTI ? ( ) : ( - <> - {activeWorkspace?.workspaceType === WorkspaceType.LOCAL ? ( - <> - - - ) : ( - - )} - + )} {viewMode === ApiClientViewMode.SINGLE && ( @@ -128,18 +96,6 @@ const WorkSpaceDropDown = ({ teamInvites }: { teamInvites: Invite[] }) => {
- - {activeWorkspace?.workspaceType === WorkspaceType.LOCAL && - isFeatureCompatible(FEATURES.LOCAL_WORKSPACE_REFRESH) ? ( - - } - /> - - ) : null} ); }; diff --git a/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/WorkspacesOverlay.tsx b/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/WorkspacesOverlay.tsx index 53f269d865..1da78783a4 100644 --- a/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/WorkspacesOverlay.tsx +++ b/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/WorkspacesOverlay.tsx @@ -11,7 +11,6 @@ import { MdOutlineGroups } from "@react-icons/all-files/md/MdOutlineGroups"; import { globalActions } from "store/slices/global/slice"; import { trackWorkspaceJoiningModalOpened } from "modules/analytics/events/features/teams"; import LoadingModal from "../LoadingModal"; -import { useCheckLocalSyncSupport } from "features/apiClient/helpers/modules/sync/useCheckLocalSyncSupport"; import { EmptyWorkspaceListView } from "./components/EmptyWorkspaceListView/EmptyWorkspaceListView"; import { CommonEmptyView } from "./components/CommonEmptyView/CommonEmptyView"; import { useWorkspaceSwitcher } from "features/apiClient/screens/apiClient/components/sidebar/MultiWorkspaceSidebar/WorkspaceProvider/useWorkspaceSwitcher"; @@ -72,8 +71,6 @@ export const WorkspacesOverlay: React.FC = ({ toggleDrop const user = useSelector(getUserAuthDetails); const availableWorkspaces = useSelector(getAllWorkspaces); - const isLocalSyncEnabled = useCheckLocalSyncSupport({ skipWorkspaceCheck: true }); - const { handleWorkspaceSwitch, confirmWorkspaceSwitch, @@ -109,7 +106,6 @@ export const WorkspacesOverlay: React.FC = ({ toggleDrop }, map); }, [sortedAvailableWorkspaces]); - const hasLocalWorkspaces = workspaceMap[WorkspaceType.LOCAL].length > 0; const hasSharedWorkspaces = workspaceMap[WorkspaceType.SHARED].length; const handleJoinWorkspaceMenuItemClick = () => { @@ -143,31 +139,18 @@ export const WorkspacesOverlay: React.FC = ({ toggleDrop )} - {!hasLocalWorkspaces && !hasSharedWorkspaces ? ( + {!hasSharedWorkspaces ? ( <> {user.loggedIn ? : null} ) : ( -
- {isLocalSyncEnabled ? ( - - confirmWorkspaceSwitch(() => handleWorkspaceSwitch(workspace, addWorkspaceToView)) - } - /> - ) : null} - - confirmWorkspaceSwitch(() => handleWorkspaceSwitch(workspace))} - /> -
+ confirmWorkspaceSwitch(() => handleWorkspaceSwitch(workspace))} + /> )} {user.loggedIn ? ( diff --git a/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/components/CommonEmptyView/CommonEmptyView.tsx b/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/components/CommonEmptyView/CommonEmptyView.tsx index ac505bb5f2..8da167878f 100644 --- a/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/components/CommonEmptyView/CommonEmptyView.tsx +++ b/app/src/layouts/DashboardLayout/MenuHeader/WorkspaceSelector/components/WorkspacesOverlay/components/CommonEmptyView/CommonEmptyView.tsx @@ -38,9 +38,7 @@ export const CommonEmptyView: React.FC = ({ toggleDropdown empty folder
You don't have any workspaces yet.
- {appMode === GLOBAL_CONSTANTS.APP_MODES.DESKTOP - ? "Create a local workspace to store files on your device or a team workspace to collaborate with teammates." - : "Create a team workspace to collaborate with teammates."} + Create a team workspace to collaborate with teammates.
= ({ toggleDropdown New team workspace - {appMode === GLOBAL_CONSTANTS.APP_MODES.DESKTOP ? ( - handleCreateWorkspace(WorkspaceType.LOCAL)}> - New local workspace - - ) : null}
);