Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App Store V2 Improvements over feat/v2-installed-apps #4379

Merged
merged 12 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/web/components/v2/apps/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import React, { useEffect, useState } from "react";

import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton } from "@calcom/app-store/components";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
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 } from "@calcom/ui/v2";
import { Button, SkeletonButton, Shell } from "@calcom/ui/v2";
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";

Expand Down
8 changes: 4 additions & 4 deletions apps/web/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ const V2_WHITELIST = [
"/apps",
"/success",
];
const V2_BLACKLIST = [
//
"/apps/routing_forms",
];

// For pages
// - which has V1 versions being modified as V2
const V2_BLACKLIST = ["/apps/routing_forms/"];

const middleware: NextMiddleware = async (req) => {
const url = req.nextUrl;
Expand Down
5 changes: 3 additions & 2 deletions apps/web/pages/v2/apps/categories/[category].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { getAppRegistry } from "@calcom/app-store/_appRegistry";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import prisma from "@calcom/prisma";
import { Icon } from "@calcom/ui/Icon";
import { SkeletonText } from "@calcom/ui/v2";
import Shell from "@calcom/ui/v2/core/Shell";
import AppCard from "@calcom/ui/v2/core/apps/AppCard";

export default function Apps({ apps }: InferGetStaticPropsType<typeof getStaticProps>) {
const { t } = useLocale();
const { t, isLocaleReady } = useLocale();
const router = useRouter();
const { category } = router.query;

Expand All @@ -22,7 +23,7 @@ export default function Apps({ apps }: InferGetStaticPropsType<typeof getStaticP
<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" />
{t("app_store")}{" "}
{isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "}
</a>
</Link>
{category && (
Expand Down
5 changes: 3 additions & 2 deletions apps/web/pages/v2/apps/categories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import { getAppRegistry } from "@calcom/app-store/_appRegistry";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
import Shell from "@calcom/ui/Shell";
import { SkeletonText } from "@calcom/ui/v2";

export default function Apps({ categories }: InferGetStaticPropsType<typeof getStaticProps>) {
const { t } = useLocale();
const { t, isLocaleReady } = useLocale();

return (
<Shell isPublic large>
<div className="text-md flex items-center gap-1 px-4 pb-3 pt-3 font-normal 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" />
{t("app_store")}{" "}
{isLocaleReady ? t("app_store") : <SkeletonText className="h-6 w-24" />}{" "}
</a>
</Link>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ type LicenseRequiredProps = {
const LicenseRequired = ({ children, as = "", ...rest }: LicenseRequiredProps) => {
const session = useSession();
const Component = as || Fragment;
const hasValidLicense = session.data ? session.data.hasValidLicense : null;
return (
<Component {...rest}>
{session.data?.hasValidLicense ? (
{hasValidLicense === null || hasValidLicense ? (
children
) : (
<EmptyScreen
Expand Down
14 changes: 10 additions & 4 deletions packages/features/users/components/UserV2OptInBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { useEffect, useState } from "react";

function UserV2OptInBanner() {
const [v2OptInCookie, setV2OptInCookie] = useState<boolean | null>(null);
// Only show on client-side
if (typeof document === "undefined") return null;

const hasV2OptInCookie = document.cookie.includes("calcom-v2-early-access=1");
useEffect(() => {
setV2OptInCookie(document.cookie.includes("calcom-v2-early-access=1"));
}, []);

if (hasV2OptInCookie)
if (!v2OptInCookie) {
return null;
}
if (v2OptInCookie)
return (
<p className="text-xs text-gray-400">
You&apos;re using the new version of Cal.com.{" "}
Expand Down
27 changes: 21 additions & 6 deletions packages/ui/v2/core/Shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +418,31 @@ const navigation: NavigationItemType[] = [
icon: Icon.FiGrid,
isCurrent: ({ router, item }) => {
const path = router.asPath.split("?")[0];
return path.startsWith(item.href);
// During Server rendering path is /v2/apps but on client it becomes /apps(weird..)
return (
(path.startsWith(item.href) || path.startsWith("/v2" + item.href)) && !path.includes("routing_forms/")
);
},
child: [
{
name: "app_store",
href: "/apps",
isCurrent: ({ router, item }) => {
const path = router.asPath.split("?")[0];
// During Server rendering path is /v2/apps but on client it becomes /apps(weird..)
return (
(path.startsWith(item.href) || path.startsWith("/v2" + item.href)) &&
!path.includes("routing_forms/") &&
!path.includes("/installed")
);
},
},
{
name: "installed_apps",
href: "/apps/installed/calendar",
isCurrent: ({ router }) => {
return router.pathname.startsWith("/apps/installed/");
const path = router.asPath;
return path.startsWith("/apps/installed/") || path.startsWith("/v2/apps/installed/");
},
},
],
Expand Down Expand Up @@ -542,7 +555,6 @@ const NavigationItem: React.FC<{
</Link>
{item.child &&
isCurrent({ router, isChild, item }) &&
router.asPath.startsWith(item.href) &&
item.child.map((item) => <NavigationItem key={item.name} item={item} isChild />)}
</Fragment>
);
Expand Down Expand Up @@ -652,7 +664,10 @@ function DeploymentInfo() {
function SideBarContainer() {
const { status } = useSession();
const router = useRouter();
if (status !== "authenticated") return null;
// Make sure that Sidebar is rendered optimistically so that a refresh of pages when logged in have SideBar from the beginning.
// This improves the experience of refresh on app store pages(when logged in) which are SSG.
// Though when logged out, app store pages would temporarily show SideBar until session status is confirmed.
if (status !== "loading" && status !== "authenticated") return null;
if (router.route.startsWith("/v2/settings/")) return null;
return <SideBar />;
}
Expand Down Expand Up @@ -741,12 +756,12 @@ export function ShellMain(props: LayoutProps) {
<div className="hidden 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 ? null : props.heading}
{!isLocaleReady ? <SkeletonText invisible /> : props.heading}
</h1>
)}
{props.subtitle && (
<p className="hidden text-sm text-neutral-500 sm:block">
{!isLocaleReady ? null : props.subtitle}
{!isLocaleReady ? <SkeletonText invisible /> : props.subtitle}
</p>
)}
</div>
Expand Down
6 changes: 4 additions & 2 deletions packages/ui/v2/core/apps/AppCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { App } from "@calcom/types/App";
import { Icon } from "@calcom/ui/Icon";
import Button from "@calcom/ui/v2/core/Button";

import { SkeletonText } from "../skeleton";

interface AppCardProps {
app: App;
credentials?: Credential[];
Expand All @@ -17,7 +19,7 @@ interface AppCardProps {
export default function AppCard({ app, credentials }: AppCardProps) {
const { t } = useLocale();
const { data: user } = trpc.useQuery(["viewer.me"]);

const { isLocaleReady } = useLocale();
const mutation = useAddAppMutation(null, {
onSuccess: () => {
showToast(t("app_successfully_installed"), "success");
Expand Down Expand Up @@ -56,7 +58,7 @@ export default function AppCard({ app, credentials }: AppCardProps) {
</p>
<div className="mt-5 flex max-w-full flex-row justify-between gap-2 [@media(max-width:260px)]:flex-wrap">
<Button color="secondary" className="flex w-32 flex-grow justify-center" href={"/apps/" + app.slug}>
{t("details")}
{isLocaleReady ? t("details") : <SkeletonText className="h-4 w-24" />}
</Button>
{app.isGlobal || (credentials && credentials.length > 0 && allowedMultipleInstalls)
? !app.isGlobal && (
Expand Down
11 changes: 8 additions & 3 deletions packages/ui/v2/core/apps/Categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ArrowRight } from "react-feather";

import { useLocale } from "@calcom/lib/hooks/useLocale";

import { SkeletonText } from "../skeleton";
import Slider from "./Slider";

export default function AppStoreCategories({
Expand All @@ -13,7 +14,7 @@ export default function AppStoreCategories({
count: number;
}[];
}) {
const { t } = useLocale();
const { t, isLocaleReady } = useLocale();
return (
<div className="mb-16">
<Slider
Expand All @@ -35,9 +36,13 @@ export default function AppStoreCategories({
data-testid={`app-store-category-${category.name}`}
className="bg-gradient-from-bl relative flex rounded-md bg-gradient-to-tr from-neutral-300 to-slate-500 sm:block">
<div className="w-full self-center bg-[url('/noise.png')] bg-cover bg-center bg-no-repeat px-6 py-4">
<h3 className="font-medium capitalize">{category.name}</h3>
{isLocaleReady ? (
<h3 className="font-medium capitalize">{category.name}</h3>
) : (
<SkeletonText invisible />
)}
<p className="text-sm text-gray-500">
{t("number_apps", { count: category.count })}{" "}
{isLocaleReady ? t("number_apps", { count: category.count }) : <SkeletonText invisible />}{" "}
<ArrowRight className="inline-block h-4 w-4" />
</p>
</div>
Expand Down
17 changes: 12 additions & 5 deletions packages/ui/v2/core/apps/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import "@glidejs/glide/dist/css/glide.core.min.css";
import "@glidejs/glide/dist/css/glide.theme.min.css";
import { useEffect, useRef } from "react";

import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";

import { SkeletonText } from "../skeleton";

const Slider = <T extends string | unknown>({
title = "",
className = "",
Expand All @@ -22,7 +25,7 @@ const Slider = <T extends string | unknown>({
}) => {
const glide = useRef(null);
const slider = useRef<Glide.Properties | null>(null);

const { isLocaleReady } = useLocale();
useEffect(() => {
if (glide.current) {
slider.current = new Glide(glide.current, {
Expand All @@ -45,10 +48,14 @@ const Slider = <T extends string | unknown>({
</style>
<div className="glide" ref={glide}>
<div className="flex cursor-default">
{title && (
<div>
<h2 className="mt-0 mb-2 text-lg font-semibold text-gray-900">{title}</h2>
</div>
{isLocaleReady ? (
title && (
<div>
<h2 className="mt-0 mb-2 text-lg font-semibold text-gray-900">{title}</h2>
</div>
)
) : (
<SkeletonText className="h-4 w-24" />
)}
<div className="glide__arrows ml-auto" data-glide-el="controls">
<button data-glide-dir="<" className="mr-4">
Expand Down
6 changes: 4 additions & 2 deletions packages/ui/v2/core/navigation/tabs/HorizontalTabItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { MouseEventHandler } from "react";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";

import { SkeletonText } from "../../skeleton";

export type HorizontalTabItemProps = {
name: string;
disabled?: boolean;
Expand All @@ -25,7 +27,7 @@ export type HorizontalTabItemProps = {

const HorizontalTabItem = ({ name, href, tabName, ...props }: HorizontalTabItemProps) => {
const router = useRouter();
const { t } = useLocale();
const { t, isLocaleReady } = useLocale();
let newHref = "";
let isCurrent;
if (href) {
Expand Down Expand Up @@ -59,7 +61,7 @@ const HorizontalTabItem = ({ name, href, tabName, ...props }: HorizontalTabItemP
props.className
)}
aria-current={isCurrent ? "page" : undefined}>
{t(name)}
{isLocaleReady ? t(name) : <SkeletonText className="h-4 w-24" />}
</a>
</Link>
);
Expand Down
8 changes: 6 additions & 2 deletions packages/ui/v2/core/skeleton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ const SkeletonAvatar: React.FC<SkeletonBaseProps> = ({ className }) => {
return <div className={classNames(`mt-1 rounded-full bg-gray-200 ltr:mr-2 rtl:ml-2`, className)} />;
};

const SkeletonText: React.FC<SkeletonBaseProps> = ({ className = "" }) => {
const SkeletonText: React.FC<SkeletonBaseProps & { invisible?: boolean }> = ({
className = "",
invisible = false,
}) => {
return (
<span
className={classNames(
`font-size-0 dark:white-300 animate-pulse rounded-md bg-gray-300 empty:before:inline-block empty:before:content-['']`,
className
className,
invisible ? "invisible" : ""
)}
/>
);
Expand Down