Hide mobile nav on subpages based on backPath (#4695)

* Hide nav on subpages based on backPath. Add backPath where it was missing

* Fix heading not showing on mobile

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
Hariom BalharaandPeer Richelsen authored and GitHub committed 2022-09-24 11:55:50 +00:00
1 parent 24635af730
commit ab417ac267
6 files changed
+241 -246

No files matched your search

+205 -217
View File
@@ -1,6 +1,6 @@
import Link from "next/link";
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton } from "@calcom/app-store/components";
@@ -9,7 +9,6 @@ import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { App as AppType } from "@calcom/types/App";
import Badge from "@calcom/ui/Badge";
import { Icon } from "@calcom/ui/Icon";
import { showToast, SkeletonText } from "@calcom/ui/v2";
import { Button, SkeletonButton, Shell } from "@calcom/ui/v2";
@@ -37,7 +36,6 @@ const Component = ({
images,
}: Parameters<typeof App>[0]) => {
const { t } = useLocale();
const { data: user } = trpc.useQuery(["viewer.me"]);
const hasImages = images && images.length > 0;
const router = useRouter();
@@ -66,225 +64,213 @@ const Component = ({
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
return (
<>
<div className="px-4 pb-3 pt-8 sm:pt-2 md:px-8 lg:px-0 lg:pt-0">
<Link href="/apps">
<a className="text-md hover:text-brand-400 inline-flex rounded-sm py-2 font-semibold text-gray-900">
<Icon.FiArrowLeft className="mr-2 h-6 w-6" /> {t("app_store")}
</a>
</Link>
</div>
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasImages && (
<div className="flex-2 mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto min-w-[672px] snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:rounded-md">
{images ? (
images.map((img) => (
<img
key={img}
src={img}
alt={`Screenshot of app ${name}`}
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
/>
))
) : (
<SkeletonText />
)}
</div>
)}
<div
className={classNames(
"sticky top-0 -mt-4 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
hasImages && "lg:ml-8"
)}>
<div className="mb-8 flex pt-4">
<header>
<div className="mb-4 flex items-center">
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} />
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1>
</div>
<h2 className="text-sm font-medium text-gray-600">
<Link href={`categories/${categories[0]}`}>
<a className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800">
{categories[0]}
</a>
</Link>{" "}
• {t("published_by", { author })}
</h2>
</header>
</div>
{!appCredentials.isLoading ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
{existingCredentials.length > 0
? t("active_install", { count: existingCredentials.length })
: t("default")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<Button
StartIcon={Icon.FiPlus}
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base"
data-testid="install-app-button">
{t("install_another")}
</Button>
);
}}
/>
)}
</div>
) : existingCredentials.length > 0 ? (
<DisconnectIntegration
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
router.replace("/apps/installed");
}}
/>
) : (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{t("install_app")}
</Button>
);
}}
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasImages && (
<div className="flex-2 mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto min-w-[672px] snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:rounded-md">
{images ? (
images.map((img) => (
<img
key={img}
src={img}
alt={`Screenshot of app ${name}`}
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
/>
))
) : (
<SkeletonButton className="h-10 w-24" />
<SkeletonText />
)}
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</span>
)}
<div className="prose prose-sm mt-8">{body}</div>
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4>
<span>
{price === 0 ? (
"Free"
) : (
<>
{Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</span>
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-sm font-normal text-black no-underline hover:underline"
href={docs}>
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={website}>
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={"mailto:" + email}>
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={tos}>
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={privacy}>
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="my-8" />
<span className="leading-1 block text-xs text-gray-500">{t("every_app_published")}</span>
<a className="mt-2 block text-xs text-red-500" href="mailto:help@cal.com">
<Icon.FiFlag className="inline h-3 w-3" /> {t("report_app")}
</a>
</div>
)}
<div
className={classNames(
"sticky top-0 -mt-4 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
hasImages && "lg:ml-8"
)}>
<div className="mb-8 flex pt-4">
<header>
<div className="mb-4 flex items-center">
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} />
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1>
</div>
<h2 className="text-sm font-medium text-gray-600">
<Link href={`categories/${categories[0]}`}>
<a className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800">{categories[0]}</a>
</Link>{" "}
• {t("published_by", { author })}
</h2>
</header>
</div>
{!appCredentials.isLoading ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
{existingCredentials.length > 0
? t("active_install", { count: existingCredentials.length })
: t("default")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<Button
StartIcon={Icon.FiPlus}
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base"
data-testid="install-app-button">
{t("install_another")}
</Button>
);
}}
/>
)}
</div>
) : existingCredentials.length > 0 ? (
<DisconnectIntegration
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
router.replace("/apps/installed");
}}
/>
) : (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{t("install_app")}
</Button>
);
}}
/>
))
) : (
<SkeletonButton className="h-10 w-24" />
)}
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</span>
)}
<div className="prose prose-sm mt-8">{body}</div>
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4>
<span>
{price === 0 ? (
"Free"
) : (
<>
{Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</span>
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-sm font-normal text-black no-underline hover:underline"
href={docs}>
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={website}>
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={"mailto:" + email}>
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={tos}>
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={privacy}>
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="my-8" />
<span className="leading-1 block text-xs text-gray-500">{t("every_app_published")}</span>
<a className="mt-2 block text-xs text-red-500" href="mailto:help@cal.com">
<Icon.FiFlag className="inline h-3 w-3" /> {t("report_app")}
</a>
</div>
</>
</div>
);
};
@@ -311,8 +297,10 @@ export default function App(props: {
isProOnly: AppType["isProOnly"];
images?: string[];
}) {
const { t } = useLocale();
return (
<Shell large isPublic>
<Shell large isPublic heading={t("app_store")} backPath="/apps">
{props.licenseRequired ? (
<LicenseRequired>
<Component {...props} />
@@ -18,21 +18,25 @@ export default function Apps({ apps }: InferGetStaticPropsType<typeof getStaticP
return (
<>
<Shell isPublic large>
<div className="text-md flex items-center gap-1 px-4 pb-3 pt-3 font-semibold md:px-8 lg:px-0 lg:pt-0">
<Link href="/apps">
<a className="inline-flex items-center justify-start gap-1 rounded-sm py-2 text-gray-900">
<Icon.FiArrowLeft className="h-4 w-4" />
{isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "}
</a>
</Link>
{category && (
<span className="flex gap-1 text-gray-600">
<span>&nbsp;/&nbsp;</span>
{t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })}
</span>
)}
</div>
<Shell
isPublic
backPath="/apps"
heading={
<>
<Link href="/apps">
<a className="inline-flex items-center justify-start gap-1 rounded-sm py-2 text-gray-900">
{isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "}
</a>
</Link>
{category && (
<span className="gap-1 text-gray-600">
<span>&nbsp;/&nbsp;</span>
{t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })}
</span>
)}
</>
}
large>
<div className="mb-16">
<div className="grid-col-1 grid grid-cols-1 gap-3 md:grid-cols-3">
{apps.map((app) => {
@@ -294,7 +294,11 @@ export default function FormEditPage({
}
FormEditPage.getLayout = (page: React.ReactElement) => {
return <Shell withoutMain={true}>{page}</Shell>;
return (
<Shell backPath="/apps/routing-forms/forms" withoutMain={true}>
{page}
</Shell>
);
};
export const getServerSideProps = async function getServerSideProps(
@@ -456,7 +456,11 @@ export default function RouteBuilder({
}
RouteBuilder.getLayout = (page: React.ReactElement) => {
return <Shell withoutMain={true}>{page}</Shell>;
return (
<Shell backPath="/apps/routing-forms/forms" withoutMain={true}>
{page}
</Shell>
);
};
export const getServerSideProps = async function getServerSideProps(
@@ -185,6 +185,7 @@ function WorkflowPage() {
});
}}>
<Shell
backPath="/workflows"
title={workflow && workflow.name ? workflow.name : "Untitled"}
CTA={
<div>
+6 -12
View File
@@ -575,16 +575,12 @@ function MobileNavigationContainer() {
const MobileNavigation = () => {
const isEmbed = useIsEmbed();
const router = useRouter();
const isSubNav = router.pathname.split("/").length > 3;
return (
<>
<nav
className={classNames(
isSubNav
? "hidden"
: "bottom-nav fixed bottom-0 z-30 -mx-4 flex w-full border border-t border-gray-200 bg-gray-50 bg-opacity-40 px-1 shadow backdrop-blur-md md:hidden",
"bottom-nav fixed bottom-0 z-30 -mx-4 flex w-full border border-t border-gray-200 bg-gray-50 bg-opacity-40 px-1 shadow backdrop-blur-md md:hidden",
isEmbed && "hidden"
)}>
{mobileNavigationBottomItems.map((item) => (
@@ -763,7 +759,7 @@ export function ShellMain(props: LayoutProps) {
"mb-4 flex w-full items-center pt-4 md:p-0 lg:mb-10"
)}>
{props.HeadingLeftIcon && <div className="ltr:mr-4">{props.HeadingLeftIcon}</div>}
<div className="hidden w-full ltr:mr-4 rtl:ml-4 sm:block">
<div className="w-full ltr:mr-4 rtl:ml-4 sm:block">
{props.heading && (
<h1 className="font-cal mb-1 text-xl font-bold capitalize tracking-wide text-black">
{!isLocaleReady ? <SkeletonText invisible /> : props.heading}
@@ -784,9 +780,6 @@ export function ShellMain(props: LayoutProps) {
)}
</div>
<div className={classNames(props.flexChildrenContainer && "flex flex-1 flex-col")}>
{/* add padding to top for mobile when App Bar is fixed */}
<div className="pt-8 sm:hidden" />
{props.children}
</div>
</>
@@ -820,11 +813,12 @@ function MainContainer({
{SettingsSidebarContainerProp}
<div className="px-4 py-2 lg:py-8 lg:px-12">
<ErrorBoundary>
{/* add padding to top for mobile when App Bar is fixed */}
<div className="pt-14 sm:hidden" />
{!props.withoutMain ? <ShellMain {...props}>{props.children}</ShellMain> : props.children}
</ErrorBoundary>
{/* show bottom navigation for md and smaller (tablet and phones) */}
{MobileNavigationContainerProp}
{/* <LicenseBanner /> */}
{/* show bottom navigation for md and smaller (tablet and phones) on pages where back button doesn't exist */}
{!props.backPath ? MobileNavigationContainerProp : null}
</div>
</main>
);