import React, { useRef, useEffect, useState } from "react"; import { Link, NavLink, useLocation } from "react-router-dom"; import { Helmet } from "react-helmet"; import styled from "styled-components"; import StyledHeader from "components/designSystems/appsmith/StyledHeader"; import AppsmithLogo from "assets/images/appsmith_logo_white.png"; import Button from "components/editorComponents/Button"; import { EDIT_APP, FORK_APP, SIGN_IN } from "constants/messages"; import { isPermitted, PERMISSION_TYPE, } from "pages/Applications/permissionHelpers"; import { ApplicationPayload, PageListPayload, } from "constants/ReduxActionConstants"; import { APPLICATIONS_URL, AUTH_LOGIN_URL, getApplicationViewerPageURL, SIGN_UP_URL, } from "constants/routes"; import { connect } from "react-redux"; import { AppState } from "reducers"; import { getEditorURL } from "selectors/appViewSelectors"; import { getPageList } from "selectors/editorSelectors"; import { FormDialogComponent } from "components/editorComponents/form/FormDialogComponent"; import AppInviteUsersForm from "pages/organization/AppInviteUsersForm"; import { getCurrentOrgId } from "selectors/organizationSelectors"; import { HeaderIcons } from "icons/HeaderIcons"; import { Colors } from "constants/Colors"; import { getCurrentUser } from "selectors/usersSelectors"; import { ANONYMOUS_USERNAME, User } from "constants/userConstants"; import { isEllipsisActive } from "utils/helpers"; import TooltipComponent from "components/ads/Tooltip"; import Text, { TextType } from "components/ads/Text"; import { Classes } from "components/ads/common"; const HeaderWrapper = styled(StyledHeader)<{ hasPages: boolean }>` background: ${Colors.BALTIC_SEA}; height: ${(props) => (props.hasPages ? "90px" : "48px")}; color: white; flex-direction: column; box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.05); .${Classes.TEXT} { max-width: 194px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #d4d4d4; } `; const HeaderRow = styled.div<{ justify: string }>` width: 100%; display: flex; flex: 1; flex-direction: row; justify-content: ${(props) => props.justify}; `; const HeaderSection = styled.div<{ justify: string }>` display: flex; flex: 1; align-items: center; justify-content: ${(props) => props.justify}; `; const AppsmithLogoImg = styled.img` max-width: 110px; `; const BackToEditorButton = styled(Button)` max-width: 200px; height: 32px; margin: 5px 10px; `; const ForkButton = styled(Button)` max-width: 200px; height: 32px; margin: 5px 10px; svg { transform: rotate(-90deg); } `; const ShareButton = styled(Button)` height: 32px; margin: 5px 10px; color: white !important; `; const PageTab = styled(NavLink)` display: flex; height: 30px; max-width: 170px; margin-right: 1px; align-self: flex-end; cursor: pointer; align-items: center; justify-content: center; text-decoration: none; background-color: rgb(49, 48, 51); padding: 0px 10px; && span { font-weight: 500; font-size: 12px; line-height: 20px; letter-spacing: 0.04em; color: #fff; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } &&&:hover { text-decoration: none; background-color: #fff; span { color: #2e3d49; } } &&&.is-active { background-color: white; span { color: #2e3d49; } } `; type AppViewerHeaderProps = { url?: string; currentApplicationDetails?: ApplicationPayload; pages: PageListPayload; currentOrgId: string; currentUser?: User; }; const PageTabName: React.FunctionComponent<{ name: string }> = ({ name }) => { const tabNameRef = useRef(null); const [ellipsisActive, setEllipsisActive] = useState(false); const tabNameText = {name}; useEffect(() => { if (isEllipsisActive(tabNameRef?.current)) { setEllipsisActive(true); } }, [tabNameRef]); return ellipsisActive ? ( {tabNameText} ) : ( <>{tabNameText} ); }; export const AppViewerHeader = (props: AppViewerHeaderProps) => { const { currentApplicationDetails, pages, currentOrgId, currentUser } = props; const isExampleApp = currentApplicationDetails?.appIsExample; const userPermissions = currentApplicationDetails?.userPermissions ?? []; const permissionRequired = PERMISSION_TYPE.MANAGE_APPLICATION; const canEdit = isPermitted(userPermissions, permissionRequired); const queryParams = new URLSearchParams(useLocation().search); const hideHeader = !!queryParams.get("embed"); const HtmlTitle = () => { if (!currentApplicationDetails?.name) return null; return ( {currentApplicationDetails?.name} ); }; if (hideHeader) return ; // Mark default page as first page const appPages = pages; if (appPages.length > 1) { appPages.forEach(function(item, i) { if (item.isDefault) { appPages.splice(i, 1); appPages.unshift(item); } }); } const forkAppUrl = `${window.location.origin}${SIGN_UP_URL}?appId=${currentApplicationDetails?.id}`; const loginAppUrl = `${window.location.origin}${AUTH_LOGIN_URL}?appId=${currentApplicationDetails?.id}`; let CTA = null; if (props.url && canEdit) { CTA = ( ); } else if (isExampleApp) { CTA = ( ); } else if ( currentApplicationDetails?.isPublic && currentUser?.username === ANONYMOUS_USERNAME ) { CTA = ( ); } return ( 1}> {currentApplicationDetails && ( {currentApplicationDetails.name} )} {currentApplicationDetails && ( <> } /> } Form={AppInviteUsersForm} orgId={currentOrgId} applicationId={currentApplicationDetails.id} title={currentApplicationDetails.name} canOutsideClickClose={true} /> {CTA} )} {appPages.length > 1 && ( {appPages.map((page) => ( ))} )} ); }; const mapStateToProps = (state: AppState): AppViewerHeaderProps => ({ pages: getPageList(state), url: getEditorURL(state), currentApplicationDetails: state.ui.applications.currentApplication, currentOrgId: getCurrentOrgId(state), currentUser: getCurrentUser(state), }); export default connect(mapStateToProps)(AppViewerHeader);