refactor: move apps link out of menu & refactor nav menu data (#34313)

## Description
Moved `Back to all apps` from  dropdown menu directly to header.

Additionally renamed `NavigationMenuData.ts` to
`useNavigationMenuData.ts` and fixed dependency related memoization
issues.

<img width="267" alt="image"
src="https://github.com/appsmithorg/appsmith/assets/173164/020abfe3-1359-43ff-aa99-972a45f270bd">

Fixes #32982

## Automation

/ok-to-test tags="@tag.IDE"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/9567752111>
> Commit: 824b2fe1231de1d073a1b19054507643b325400f
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9567752111&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.IDE`

<!-- end of auto-generated comment: Cypress test results  -->



## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Refactor**
- Removed `getNavigationMenu` prop from `EditorHeader` for streamlined
navigation menu data retrieval.
- Updated `EditorName` to use `useNavigationMenuData` for navigation
menu items.
- Refactored `useNavigationMenuData` to use `useCallback` and `useMemo`
for enhanced performance.
- Adjusted imports and event handling functions in `IDE/Header` to use
`useCallback` for better efficiency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Alex 2024-06-20 10:13:35 +03:00 committed by GitHub
parent 092208a1c1
commit b85c4f5451
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 112 additions and 121 deletions

View File

@ -75,7 +75,6 @@ import { Omnibar } from "./commons/Omnibar";
import { EditorShareButton } from "./EditorShareButton";
import { HelperBarInHeader } from "./HelpBarInHeader";
import { AppsmithLink } from "./AppsmithLink";
import { GetNavigationMenuData } from "./EditorName/NavigationMenuData";
const { cloudHosting } = getAppsmithConfigs();
@ -208,7 +207,6 @@ export function EditorHeader() {
editInteractionKind={EditInteractionKind.SINGLE}
editorName="Application"
fill
getNavigationMenu={GetNavigationMenuData}
isError={isErroredSavingName}
isNewEditor={
applicationList.filter((el) => el.id === applicationId)

View File

@ -1,6 +1,5 @@
import React, { useState, useCallback } from "react";
import { useTheme } from "styled-components";
import type { noop } from "lodash";
import type {
CommonComponentProps,
@ -9,14 +8,12 @@ import type {
import { SavingState } from "design-system-old";
import EditableName from "./EditableName";
import { NavigationMenu } from "./NavigationMenu";
import type { Theme } from "constants/DefaultTheme";
import { Menu, toast, MenuTrigger } from "design-system";
import ForkApplicationModal from "pages/Applications/ForkApplicationModal";
import { Container, StyledIcon } from "./components";
import { useSelector } from "react-redux";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import type { MenuItemData } from "./NavigationMenuItem";
import type { NavigationMenuDataProps } from "./NavigationMenuData";
import { useNavigationMenuData } from "./useNavigationMenuData";
type EditorNameProps = CommonComponentProps & {
applicationId?: string | undefined;
@ -34,10 +31,6 @@ type EditorNameProps = CommonComponentProps & {
isPopoverOpen: boolean;
setIsPopoverOpen: typeof noop;
editorName: string;
getNavigationMenu: ({
editMode,
setForkApplicationModalOpen,
}: NavigationMenuDataProps) => MenuItemData[];
};
export function EditorName(props: EditorNameProps) {
@ -45,14 +38,11 @@ export function EditorName(props: EditorNameProps) {
defaultSavingState,
defaultValue,
editorName,
getNavigationMenu,
isNewEditor,
isPopoverOpen,
setIsPopoverOpen,
} = props;
const theme = useTheme() as Theme;
const [isEditingDefault, setIsEditingDefault] = useState(isNewEditor);
const [isEditing, setIsEditing] = useState(!!isEditingDefault);
const [isInvalid, setIsInvalid] = useState<string | boolean>(false);
@ -102,9 +92,8 @@ export function EditorName(props: EditorNameProps) {
}
}, []);
const NavigationMenuData = getNavigationMenu({
const navigationMenuData = useNavigationMenuData({
editMode,
theme,
setForkApplicationModalOpen,
});
@ -145,7 +134,7 @@ export function EditorName(props: EditorNameProps) {
</Container>
</MenuTrigger>
<NavigationMenu
menuItems={NavigationMenuData}
menuItems={navigationMenuData}
setIsPopoverOpen={setIsPopoverOpen}
/>
</Menu>

View File

@ -1,3 +1,5 @@
import type React from "react";
import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import type { noop } from "lodash";
@ -18,19 +20,18 @@ import { getCurrentApplication } from "@appsmith/selectors/applicationSelectors"
import { Colors } from "constants/Colors";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import { toast } from "design-system";
import type { ThemeProp } from "WidgetProvider/constants";
import { DOCS_BASE_URL } from "constants/ThirdPartyConstants";
import { getAppsmithConfigs } from "@appsmith/configs";
import { getCurrentUser } from "selectors/usersSelectors";
const { cloudHosting, intercomAppID } = getAppsmithConfigs();
export interface NavigationMenuDataProps extends ThemeProp {
export interface NavigationMenuDataProps {
editMode: typeof noop;
setForkApplicationModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
export const GetNavigationMenuData = ({
export const useNavigationMenuData = ({
editMode,
setForkApplicationModalOpen,
}: NavigationMenuDataProps): MenuItemData[] => {
@ -38,12 +39,16 @@ export const GetNavigationMenuData = ({
const history = useHistory();
const applicationId = useSelector(getCurrentApplicationId);
const isApplicationIdPresent = !!(applicationId && applicationId.length > 0);
const user = useSelector(getCurrentUser);
const { isIntercomConsentGiven } = user || {};
const currentApplication = useSelector(getCurrentApplication);
const hasDeletePermission = hasDeleteApplicationPermission(
currentApplication?.userPermissions,
);
const hasExportPermission = isPermitted(
currentApplication?.userPermissions ?? [],
PERMISSION_TYPE.EXPORT_APPLICATION,
@ -58,7 +63,7 @@ export const GetNavigationMenuData = ({
}
};
const exportAppAsJSON = () => {
const exportAppAsJSON = useCallback(() => {
const id = `t--export-app-link`;
const existingLink = document.getElementById(id);
existingLink && existingLink.remove();
@ -75,9 +80,13 @@ export const GetNavigationMenuData = ({
toast.show(`Successfully exported ${currentApplication?.name}`, {
kind: "success",
});
};
}, [
applicationId,
currentApplication?.gitApplicationMetadata?.branchName,
currentApplication?.name,
]);
const deleteApplication = () => {
const deleteApplication = useCallback(() => {
if (applicationId && applicationId.length > 0) {
dispatch({
type: ReduxActionTypes.DELETE_APPLICATION_INIT,
@ -91,85 +100,88 @@ export const GetNavigationMenuData = ({
kind: "error",
});
}
};
}, [applicationId, dispatch, history]);
return [
{
text: "Back to all apps",
onClick: () => history.replace(APPLICATIONS_URL),
type: MenuTypes.MENU,
isVisible: true,
},
{
text: "divider_1",
type: MenuTypes.MENU_DIVIDER,
isVisible: true,
},
{
text: "Rename",
onClick: editMode,
type: MenuTypes.MENU,
isVisible: true,
},
{
text: "Fork application",
onClick: () => setForkApplicationModalOpen(true),
type: MenuTypes.MENU,
isVisible: isApplicationIdPresent && hasEditPermission,
},
{
text: "Export application",
onClick: exportAppAsJSON,
type: MenuTypes.MENU,
isVisible: isApplicationIdPresent && hasExportPermission,
},
hasDeleteApplicationPermission(currentApplication?.userPermissions) && {
text: "Delete application",
confirmText: "Are you sure?",
onClick: deleteApplication,
type: MenuTypes.RECONFIRM,
isVisible: isApplicationIdPresent,
style: { color: Colors.ERROR_RED },
},
{
text: "divider_2",
type: MenuTypes.MENU_DIVIDER,
isVisible: true,
},
{
text: "Help",
type: MenuTypes.PARENT,
isVisible: true,
children: [
return useMemo(
() =>
[
{
text: "Documentation",
onClick: () => openExternalLink(DOCS_BASE_URL),
text: "Rename",
onClick: editMode,
type: MenuTypes.MENU,
isVisible: true,
startIcon: "book-line",
},
{
text: "Report a bug",
onClick: () =>
openExternalLink(
"https://github.com/appsmithorg/appsmith/issues/new/choose",
),
text: "Fork application",
onClick: () => setForkApplicationModalOpen(true),
type: MenuTypes.MENU,
isVisible: isApplicationIdPresent && hasEditPermission,
},
{
text: "Export application",
onClick: exportAppAsJSON,
type: MenuTypes.MENU,
isVisible: isApplicationIdPresent && hasExportPermission,
},
hasDeletePermission && {
text: "Delete application",
confirmText: "Are you sure?",
onClick: deleteApplication,
type: MenuTypes.RECONFIRM,
isVisible: isApplicationIdPresent,
style: { color: Colors.ERROR_RED },
},
{
text: "divider_2",
type: MenuTypes.MENU_DIVIDER,
isVisible: true,
startIcon: "bug-line",
},
{
startIcon: "chat-help",
text: "Chat with us",
onClick: () => {
if (cloudHosting || user?.isIntercomConsentGiven) {
window.Intercom("show");
}
},
type: MenuTypes.MENU,
isVisible: intercomAppID && window.Intercom,
text: "Help",
type: MenuTypes.PARENT,
isVisible: true,
children: [
{
text: "Documentation",
onClick: () => openExternalLink(DOCS_BASE_URL),
type: MenuTypes.MENU,
isVisible: true,
startIcon: "book-line",
},
{
text: "Report a bug",
onClick: () =>
openExternalLink(
"https://github.com/appsmithorg/appsmith/issues/new/choose",
),
type: MenuTypes.MENU,
isVisible: true,
startIcon: "bug-line",
},
{
startIcon: "chat-help",
text: "Chat with us",
onClick: () => {
if (cloudHosting || isIntercomConsentGiven) {
window.Intercom("show");
}
},
type: MenuTypes.MENU,
isVisible: intercomAppID && window.Intercom,
},
],
},
],
},
].filter(Boolean) as MenuItemData[];
].filter(Boolean) as MenuItemData[],
[
editMode,
isApplicationIdPresent,
hasEditPermission,
exportAppAsJSON,
hasExportPermission,
hasDeletePermission,
deleteApplication,
setForkApplicationModalOpen,
isIntercomConsentGiven,
],
);
};

View File

@ -2,7 +2,6 @@ import React, { useCallback, useState } from "react";
import {
Flex,
Tooltip,
Text,
Divider,
Modal,
ModalContent,
@ -13,6 +12,7 @@ import {
Tab,
TabPanel,
Button,
Link,
} from "design-system";
import { useDispatch, useSelector } from "react-redux";
import { EditInteractionKind, SavingState } from "design-system-old";
@ -29,7 +29,6 @@ import {
HEADER_TITLES,
} from "@appsmith/constants/messages";
import EditorName from "pages/Editor/EditorName";
import { GetNavigationMenuData } from "pages/Editor/EditorName/NavigationMenuData";
import {
getCurrentApplicationId,
getCurrentPageId,
@ -76,6 +75,7 @@ import { EditorState } from "@appsmith/entities/IDE/constants";
import { EditorSaveIndicator } from "pages/Editor/EditorSaveIndicator";
import type { Page } from "@appsmith/constants/ReduxActionConstants";
import { IDEHeader, IDEHeaderTitle } from "IDE";
import { APPLICATIONS_URL } from "constants/routes";
const StyledDivider = styled(Divider)`
height: 50%;
@ -166,7 +166,7 @@ const Header = () => {
dispatch(updateApplication(id, data));
};
const handlePublish = () => {
const handlePublish = useCallback(() => {
if (applicationId) {
dispatch(publishApplication(applicationId));
@ -201,23 +201,18 @@ const Header = () => {
templateTitle: currentApplication?.forkedFromTemplateTitle,
});
}
};
}, [applicationId, currentApplication, dispatch]);
const handleClickDeploy = useCallback(
(fromDeploy?: boolean) => {
if (isGitConnected) {
dispatch(showConnectGitModal());
AnalyticsUtil.logEvent("GS_DEPLOY_GIT_CLICK", {
source: fromDeploy
? "Deploy button"
: "Application name menu (top left)",
});
} else {
handlePublish();
}
},
[dispatch, handlePublish],
);
const handleClickDeploy = useCallback(() => {
if (isGitConnected) {
dispatch(showConnectGitModal());
AnalyticsUtil.logEvent("GS_DEPLOY_GIT_CLICK", {
source: "Deploy button",
});
} else {
handlePublish();
}
}, [dispatch, handlePublish, isGitConnected]);
return (
<>
@ -230,12 +225,10 @@ const Header = () => {
<Flex alignItems={"center"}>
{currentWorkspace.name && (
<>
<Text
color={"var(--ads-v2-colors-content-label-inactive-fg)"}
kind="body-m"
>
{currentWorkspace.name + " / "}
</Text>
<Link className="mr-1.5" to={APPLICATIONS_URL}>
{currentWorkspace.name}
</Link>
{"/"}
<EditorName
applicationId={applicationId}
className="t--application-name editable-application-name max-w-48"
@ -246,7 +239,6 @@ const Header = () => {
editInteractionKind={EditInteractionKind.SINGLE}
editorName="Application"
fill
getNavigationMenu={GetNavigationMenuData}
isError={isErroredSavingName}
isNewEditor={
applicationList.filter((el) => el.id === applicationId)
@ -343,7 +335,7 @@ const Header = () => {
isDisabled={isProtectedMode}
isLoading={isPublishing}
kind="tertiary"
onClick={() => handleClickDeploy(true)}
onClick={handleClickDeploy}
size="md"
startIcon={"rocket"}
>