From 52b2ce8774ec15b6131212e672e49b2129a5f62e Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Tue, 6 Sep 2022 00:36:34 +0530 Subject: [PATCH] V2.0 Shell - Progressive Rendering with Skeleton (#4138) * Fix Routing * Update up and down icons * Add Shell Progressive loading * Cleanup of custom loader * Remove skeleton for heading and subtitle * Replaces isI18nLoading for isLocaleReady Co-authored-by: Peer Richelsen Co-authored-by: zomars --- .../components/eventtype/CreateEventType.tsx | 8 +-- apps/web/lib/QueryCell.tsx | 18 ++++--- apps/web/pages/apps/installed.tsx | 6 +-- apps/web/pages/availability/index.tsx | 6 +-- apps/web/pages/bookings/[status].tsx | 2 +- apps/web/pages/event-types/index.tsx | 3 +- apps/web/pages/v2/availability/index.tsx | 6 +-- apps/web/pages/v2/bookings/[status].tsx | 5 +- packages/ui/skeleton/index.tsx | 10 ++-- packages/ui/v2/core/Shell.tsx | 53 +++++++++++-------- packages/ui/v2/core/form/FormCard.tsx | 4 +- packages/ui/v2/core/skeleton/index.tsx | 10 ++-- 12 files changed, 66 insertions(+), 65 deletions(-) diff --git a/apps/web/components/eventtype/CreateEventType.tsx b/apps/web/components/eventtype/CreateEventType.tsx index 237b51d726..19b9a62451 100644 --- a/apps/web/components/eventtype/CreateEventType.tsx +++ b/apps/web/components/eventtype/CreateEventType.tsx @@ -7,7 +7,6 @@ import { useForm } from "react-hook-form"; import type { z } from "zod"; import classNames from "@calcom/lib/classNames"; -import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype"; @@ -49,9 +48,8 @@ interface CreateEventTypeBtnProps { } export default function CreateEventTypeButton(props: CreateEventTypeBtnProps) { - const { t } = useLocale(); + const { t, isLocaleReady } = useLocale(); const router = useRouter(); - // URL encoded params const teamId: number | undefined = typeof router.query.teamId === "string" && router.query.teamId @@ -139,7 +137,9 @@ export default function CreateEventTypeButton(props: CreateEventTypeBtnProps) { { shallow: true } ); }; - + if (!isLocaleReady) { + return null; + } return ( { error?: ( query: QueryObserverLoadingErrorResult | QueryObserverRefetchErrorResult ) => JSXElementOrNull; - loading?: (query: QueryObserverLoadingResult) => JSXElementOrNull; + loading?: (query: QueryObserverLoadingResult | null) => JSXElementOrNull; idle?: (query: QueryObserverIdleResult) => JSXElementOrNull; } @@ -61,12 +62,20 @@ export function QueryCell( opts: QueryCellOptionsNoEmpty | QueryCellOptionsWithEmpty ) { const { query } = opts; + const { isLocaleReady } = useLocale(); + const StatusLoader = opts.customLoader || ; // Fixes edge case where this can return null form query cell + + if (query.status === "loading" || !isLocaleReady) { + return opts.loading?.(query.status === "loading" ? query : null) ?? StatusLoader; + } + if (query.status === "success") { if ("empty" in opts && (query.data == null || (Array.isArray(query.data) && query.data.length === 0))) { return opts.empty(query); } return opts.success(query as any); } + if (query.status === "error") { return ( opts.error?.(query) ?? ( @@ -74,11 +83,7 @@ export function QueryCell( ) ); } - const StatusLoader = opts.customLoader || ; // Fixes edge case where this can return null form query cell - if (query.status === "loading") { - return opts.loading?.(query) ?? StatusLoader; - } if (query.status === "idle") { return opts.idle?.(query) ?? StatusLoader; } @@ -114,7 +119,6 @@ const withQuery = ( > ) { const query = trpc.useQuery(pathAndInput, params); - return ; }; }; diff --git a/apps/web/pages/apps/installed.tsx b/apps/web/pages/apps/installed.tsx index 6c2194a9bd..31c17a65de 100644 --- a/apps/web/pages/apps/installed.tsx +++ b/apps/web/pages/apps/installed.tsx @@ -143,11 +143,7 @@ export default function IntegrationsPage() { const { t } = useLocale(); const query = trpc.useQuery(["viewer.integrations", { onlyInstalled: true }]); return ( - }> + - } - customLoader={}> + }> } customLoader={} /> diff --git a/apps/web/pages/bookings/[status].tsx b/apps/web/pages/bookings/[status].tsx index 850511d043..ae8926c5bd 100644 --- a/apps/web/pages/bookings/[status].tsx +++ b/apps/web/pages/bookings/[status].tsx @@ -82,7 +82,7 @@ export default function Bookings() { return true; }; return ( - }> +
diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index b545ac7ffa..9efb02bd92 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -564,8 +564,7 @@ const EventTypesPage = () => { } - customLoader={}> + CTA={}> } success={({ data }) => ( diff --git a/apps/web/pages/v2/availability/index.tsx b/apps/web/pages/v2/availability/index.tsx index 8f3f7506e9..d7175041be 100644 --- a/apps/web/pages/v2/availability/index.tsx +++ b/apps/web/pages/v2/availability/index.tsx @@ -60,11 +60,7 @@ export default function AvailabilityPage() { const { t } = useLocale(); return (
- } - customLoader={}> + }> } customLoader={} />
diff --git a/apps/web/pages/v2/bookings/[status].tsx b/apps/web/pages/v2/bookings/[status].tsx index 2a00b01a57..534bb49440 100644 --- a/apps/web/pages/v2/bookings/[status].tsx +++ b/apps/web/pages/v2/bookings/[status].tsx @@ -80,10 +80,7 @@ export default function Bookings() { return true; }; return ( - }> +
{query.status === "error" && ( diff --git a/packages/ui/skeleton/index.tsx b/packages/ui/skeleton/index.tsx index 1811ef9cd6..43a510ef02 100644 --- a/packages/ui/skeleton/index.tsx +++ b/packages/ui/skeleton/index.tsx @@ -1,8 +1,8 @@ import classNames from "@calcom/lib/classNames"; type SkeletonBaseProps = { - width: string; - height: string; + width?: string; + height?: string; className?: string; }; @@ -29,11 +29,13 @@ const SkeletonAvatar: React.FC = ({ width, height, className }) => ); }; -const SkeletonText: React.FC = ({ width, height, className }) => { +const SkeletonText: React.FC = ({ width = "", height = "", className = "" }) => { + className = width ? `${className} w-${width}` : className; + className = height ? `${className} h-${height}` : className; return (
diff --git a/packages/ui/v2/core/Shell.tsx b/packages/ui/v2/core/Shell.tsx index 54954c7a51..039c491e5d 100644 --- a/packages/ui/v2/core/Shell.tsx +++ b/packages/ui/v2/core/Shell.tsx @@ -26,16 +26,15 @@ import Dropdown, { DropdownMenuTrigger, } from "@calcom/ui/Dropdown"; import { Icon } from "@calcom/ui/Icon"; -import { Loader } from "@calcom/ui/v2"; -import { useViewerI18n } from "@calcom/web/components/I18nLanguageHandler"; /* TODO: Get this from endpoint */ import pkg from "../../../../apps/web/package.json"; import ErrorBoundary from "../../ErrorBoundary"; -import { KBarRoot, KBarContent, KBarTrigger } from "../../Kbar"; +import { KBarContent, KBarRoot, KBarTrigger } from "../../Kbar"; import Logo from "../../Logo"; // TODO: re-introduce in 2.1 import Tips from "../modules/tips/Tips"; import HeadSeo from "./head-seo"; +import { SkeletonText } from "./skeleton"; /* TODO: Migate this */ @@ -160,7 +159,6 @@ type LayoutProps = { // use when content needs to expand with flex flexChildrenContainer?: boolean; isPublic?: boolean; - customLoader?: ReactNode; withoutMain?: boolean; }; @@ -174,7 +172,6 @@ export default function Shell(props: LayoutProps) { useRedirectToOnboardingIfNeeded(); useTheme("light"); const { session } = useRedirectToLoginIfUnauthenticated(props.isPublic); - if (!session && !props.isPublic) return null; return ( @@ -400,6 +397,9 @@ const navigation: NavigationItemType[] = [ name: "Routing Forms", href: "/apps/routing_forms/forms", icon: Icon.FiFileText, + isCurrent: ({ router }) => { + return router.asPath.startsWith("/apps/routing_forms/"); + }, }, { name: "workflows", @@ -463,7 +463,7 @@ const NavigationItem: React.FC<{ isChild?: boolean; }> = (props) => { const { item, isChild } = props; - const { t } = useLocale(); + const { t, isLocaleReady } = useLocale(); const router = useRouter(); const isCurrent: NavigationItemType["isCurrent"] = item.isCurrent || defaultIsCurrent; const current = isCurrent({ isChild: !!isChild, item, router }); @@ -490,7 +490,11 @@ const NavigationItem: React.FC<{ aria-current={current ? "page" : undefined} /> )} - {t(item.name)} + {!isLocaleReady ? ( + + ) : ( + {t(item.name)} + )} {item.child && @@ -535,10 +539,11 @@ const MobileNavigationItem: React.FC<{ }> = (props) => { const { item, itemIdx, isChild } = props; const router = useRouter(); - const { t } = useLocale(); + const { t, isLocaleReady } = useLocale(); const isCurrent: NavigationItemType["isCurrent"] = item.isCurrent || defaultIsCurrent; const current = isCurrent({ isChild: !!isChild, item, router }); const shouldDisplayNavigationItem = useShouldDisplayNavigationItem(props.item); + if (!shouldDisplayNavigationItem) return null; return ( @@ -556,7 +561,11 @@ const MobileNavigationItem: React.FC<{ aria-current={current ? "page" : undefined} /> )} - {t(item.name)} + {!isLocaleReady ? ( + {t(item.name)} + ) : ( + + )} ); @@ -590,6 +599,8 @@ function SideBarContainer() { } function SideBar() { + const { isLocaleReady } = useLocale(); + return (