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

feat: always show intercom on desktop by default (hidden on mobile) #14423

Merged
merged 11 commits into from
Apr 11, 2024
Merged
10 changes: 0 additions & 10 deletions apps/web/modules/event-types/views/event-types-listing-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { memo, useEffect, useState } from "react";
import { z } from "zod";

import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom";
import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/embed/EventTypeEmbed";
import { EventTypeDescription } from "@calcom/features/eventtypes/components";
import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog";
Expand Down Expand Up @@ -950,8 +949,6 @@ const EventTypesPage: React.FC & {
getLayout?: AppProps["Component"]["getLayout"];
} = () => {
const { t } = useLocale();
const searchParams = useCompatSearchParams();
const { open } = useIntercom();
const { data: user } = useMeQuery();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [showProfileBanner, setShowProfileBanner] = useState(false);
Expand All @@ -966,13 +963,6 @@ const EventTypesPage: React.FC & {
staleTime: 1 * 60 * 60 * 1000,
});

useEffect(() => {
if (searchParams?.get("openIntercom") === "true") {
open();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
setShowProfileBanner(
!!orgBranding && !document.cookie.includes("calcom-profile-banner=1") && !user?.completedOnboarding
Expand Down
2 changes: 2 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2375,5 +2375,7 @@
"unreviewed": "Unreviewed",
"rating_url_info":"The URL for Rating Feedback Form",
"no_show_url_info":"The URL for No Show Feedback",
"no_support_needed":"No Support Needed?",
"hide_support":"Hide Support",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
2 changes: 0 additions & 2 deletions packages/features/ee/support/components/ContactMenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Icon, UpgradeTeamsBadge } from "@calcom/ui";

import FreshChatMenuItem from "../lib/freshchat/FreshChatMenuItem";
import HelpscoutMenuItem from "../lib/helpscout/HelpscoutMenuItem";
import IntercomMenuItem from "../lib/intercom/IntercomMenuItem";
import ZendeskMenuItem from "../lib/zendesk/ZendeskMenuItem";

interface ContactMenuItem {
Expand All @@ -20,7 +19,6 @@ export default function ContactMenuItem(props: ContactMenuItem) {
<>
{hasPaidPlan ? (
<>
<IntercomMenuItem onHelpItemSelect={onHelpItemSelect} />
<ZendeskMenuItem onHelpItemSelect={onHelpItemSelect} />
<HelpscoutMenuItem onHelpItemSelect={onHelpItemSelect} />
<FreshChatMenuItem onHelpItemSelect={onHelpItemSelect} />
Expand Down
90 changes: 64 additions & 26 deletions packages/features/ee/support/components/HelpMenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { useState } from "react";
import { useChat } from "react-live-chat-loader";

import classNames from "@calcom/lib/classNames";
import { JOIN_DISCORD } from "@calcom/lib/constants";
import { useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Button, showToast, TextArea } from "@calcom/ui";
Expand All @@ -16,16 +14,18 @@ import ContactMenuItem from "./ContactMenuItem";

interface HelpMenuItemProps {
onHelpItemSelect: () => void;
showSupport: boolean;
}

export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
export default function HelpMenuItem({ onHelpItemSelect, showSupport }: HelpMenuItemProps) {
const [rating, setRating] = useState<null | string>(null);
const { open } = useIntercom();
const { open, shutdown } = useIntercom();
const [comment, setComment] = useState("");
const [disableSubmit, setDisableSubmit] = useState(true);
const [active, setActive] = useState(false);
const [, loadChat] = useChat();
const { t } = useLocale();
const utils = trpc.useContext();

const { setActive: setFreshChat } = useFreshChat();

Expand All @@ -37,6 +37,12 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
},
});

const showIntercomMutation = trpc.viewer.updateProfile.useMutation({
onSuccess: async () => {
await utils.viewer.me.invalidate();
},
});

const onRatingClick = (value: string) => {
setRating(value);
setDisableSubmit(false);
Expand All @@ -46,8 +52,6 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
mutation.mutate({ rating: rating, comment: comment });
};

const { hasPaidPlan } = useHasPaidPlan();

return (
<div className="bg-default border-default w-full rounded-md">
<div className="w-full py-5">
Expand All @@ -71,7 +75,6 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
<ContactMenuItem onHelpItemSelect={onHelpItemSelect} />
</div>
</div>

<hr className="border-muted" />
<div className="w-full p-5">
<p className="text-subtle mb-1">{t("feedback").toUpperCase()}</p>
Expand Down Expand Up @@ -188,33 +191,68 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
</div>
)}
</div>
<div className="text-subtle bg-muted w-full p-5">
<p className="">{t("specific_issue")}</p>
{hasPaidPlan ? (
<button
className="hover:text-emphasis text-defualt font-medium underline"
onClick={async () => {
setActive(true);
{/* visible on desktop */}
<div className="text-subtle bg-muted hidden w-full flex-col p-5 md:block">
<p className="">{showSupport ? t("no_support_needed") : t("specific_issue")}</p>
<button
className="hover:text-emphasis text-defualt font-medium underline"
onClick={async () => {
setActive(true);
if (showSupport) {
if (isFreshChatEnabled) {
setFreshChat(false);
} else if (isInterComEnabled) {
shutdown();
}
showIntercomMutation.mutate({
showSupport: false,
});
} else {
if (isFreshChatEnabled) {
setFreshChat(true);
} else if (isInterComEnabled) {
await open();
} else {
loadChat({ open: true });
}
showIntercomMutation.mutate({
showSupport: true,
});
}
onHelpItemSelect();
}}>
{showSupport ? t("hide_support") : t("contact_support")}
</button>
<span> {t("or").toLowerCase()} </span>
<a
onClick={() => onHelpItemSelect()}
className="hover:text-emphasis text-defualt font-medium underline"
href="https://cal.com/docs"
target="_blank"
rel="noreferrer">
{t("browse_our_docs")}
</a>
.
</div>
{/* visible on mobile */}
<div className="text-subtle bg-muted w-full p-5 md:hidden">
<p className="">{t("specific_issue")}</p>
<button
className="hover:text-emphasis text-defualt font-medium underline"
onClick={async () => {
setActive(true);
if (isFreshChatEnabled) {
setFreshChat(true);
} else if (isInterComEnabled) {
await open();
} else {
loadChat({ open: true });
}

onHelpItemSelect();
}}>
{t("contact_support")}
</button>
) : (
<a
href={JOIN_DISCORD}
target="_blank"
className="hover:text-emphasis text-defualt font-medium underline">
{t("community_support")}
</a>
)}
onHelpItemSelect();
}}>
{t("contact_support")}
</button>
<span> {t("or").toLowerCase()} </span>
<a
onClick={() => onHelpItemSelect()}
Expand Down
26 changes: 0 additions & 26 deletions packages/features/ee/support/lib/intercom/IntercomMenuItem.tsx

This file was deleted.

34 changes: 33 additions & 1 deletion packages/features/ee/support/lib/intercom/useIntercom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const useIntercomHook = isInterComEnabled
return {
boot: noop,
show: noop,
shutdown: noop,
};
};

Expand All @@ -26,6 +27,37 @@ export const useIntercom = () => {
const { hasPaidPlan } = useHasPaidPlan();
const { hasTeamPlan } = useHasTeamPlan();

const boot = async () => {
let userHash;

const req = await fetch(`/api/intercom-hash`);
const res = await req.json();
if (res?.hash) {
userHash = res.hash;
}

hookData.boot({
...(data && data?.name && { name: data.name }),
...(data && data?.email && { email: data.email }),
...(data && data?.id && { userId: data.id }),
createdAt: String(dayjs(data?.createdDate).unix()),
...(userHash && { userHash }),
customAttributes: {
//keys should be snake cased
user_name: data?.username,
link: `${WEBSITE_URL}/${data?.username}`,
admin_link: `${WEBAPP_URL}/settings/admin/users/${data?.id}/edit`,
identity_provider: data?.identityProvider,
timezone: data?.timeZone,
locale: data?.locale,
has_paid_plan: hasPaidPlan,
has_team_plan: hasTeamPlan,
metadata: data?.metadata,
is_logged_in: !!data,
},
});
};

const open = async () => {
let userHash;

Expand Down Expand Up @@ -57,7 +89,7 @@ export const useIntercom = () => {
});
hookData.show();
};
return { ...hookData, open };
return { ...hookData, open, boot };
};

export default useIntercom;
13 changes: 12 additions & 1 deletion packages/features/shell/Shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from "@calcom/features/ee/organizations/components/OrgUpgradeBanner";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import HelpMenuItem from "@calcom/features/ee/support/components/HelpMenuItem";
import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom";
import { TeamsUpgradeBanner, type TeamsUpgradeBannerProps } from "@calcom/features/ee/teams/components";
import { useFlagMap } from "@calcom/features/flags/context/provider";
import { KBarContent, KBarRoot, KBarTrigger } from "@calcom/features/kbar/Kbar";
Expand Down Expand Up @@ -52,6 +53,7 @@ import { useFormbricks } from "@calcom/lib/formbricks-client";
import getBrandColours from "@calcom/lib/getBrandColours";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import useTheme from "@calcom/lib/hooks/useTheme";
import { isKeyInObject } from "@calcom/lib/isKeyInObject";
import type { User } from "@calcom/prisma/client";
Expand Down Expand Up @@ -213,8 +215,17 @@ const useBanners = () => {
const Layout = (props: LayoutProps) => {
const banners = useBanners();

const isMobile = useMediaQuery("(max-width: 768px)");
const { data: user } = useMeQuery();
const { boot } = useIntercom();
const pageTitle = typeof props.heading === "string" && !props.title ? props.heading : props.title;

useEffect(() => {
if (user?.showSupport && !isMobile) {
boot();
}
}, [user?.showSupport, isMobile]);

const bannersHeight = useMemo(() => {
const activeBanners =
banners &&
Expand Down Expand Up @@ -476,7 +487,7 @@ function UserDropdown({ small }: UserDropdownProps) {
}}
className="group overflow-hidden rounded-md">
{helpOpen ? (
<HelpMenuItem onHelpItemSelect={() => onHelpItemSelect()} />
<HelpMenuItem onHelpItemSelect={() => onHelpItemSelect()} showSupport={user.showSupport} />
) : (
<>
<DropdownMenuItem>
Expand Down
1 change: 1 addition & 0 deletions packages/lib/test/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export const buildUser = <T extends Partial<UserPayload>>(
movedToProfileId: null,
priority: user?.priority ?? null,
isPlatformManaged: false,
showSupport: true,
...user,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "showSupport" BOOLEAN NOT NULL DEFAULT true;
1 change: 1 addition & 0 deletions packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ model User {
movedToProfile Profile? @relation("moved_to_profile", fields: [movedToProfileId], references: [id], onDelete: SetNull)
secondaryEmails SecondaryEmail[]
isPlatformManaged Boolean @default(false)
showSupport Boolean @default(true)
PeerRich marked this conversation as resolved.
Show resolved Hide resolved

@@unique([email])
@@unique([email, username])
Expand Down
1 change: 1 addition & 0 deletions packages/trpc/server/middlewares/sessionMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
allowDynamicBooking: true,
allowSEOIndexing: true,
receiveMonthlyDigestEmail: true,
showSupport: true,
},
});

Expand Down
1 change: 1 addition & 0 deletions packages/trpc/server/routers/loggedInViewer/me.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,6 @@ export const meHandler = async ({ ctx }: MeOptions) => {
profile: user.profile ?? null,
profiles: allUserEnrichedProfiles,
secondaryEmails,
showSupport: user.showSupport,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const ZUpdateProfileInputSchema = z.object({
})
)
.optional(),
showSupport: z.boolean().optional(),
});

export type TUpdateProfileInputSchema = z.infer<typeof ZUpdateProfileInputSchema>;