[WEB-5559] improvement: chat support functionality and remove Intercom provider (#8217)

* [WEB-5559] improve: chat support functionality and remove Intercom provider

- Added ChatSupportModal component for chat support integration.
- Replaced IntercomProvider with ChatSupportModal in AppProvider.
- Introduced useChatSupport hook to manage chat support state and actions.
- Updated help commands to utilize chat support instead of Intercom.
- Removed obsolete Intercom-related components and hooks.

* refactor: lazy load ChatSupportModal in AppProvider
This commit is contained in:
Prateek Shourya 2025-12-03 00:03:56 +05:30 committed by GitHub
parent e650b19933
commit cacd1b489e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 97 additions and 394 deletions

View file

@ -0,0 +1,43 @@
import { useEffect } from "react";
import { Intercom, shutdown, show } from "@intercom/messenger-js-sdk";
import { observer } from "mobx-react";
// custom events
import { CHAT_SUPPORT_EVENTS } from "@/custom-events/chat-support";
// store hooks
import { useInstance } from "@/hooks/store/use-instance";
import { useUser } from "@/hooks/store/user";
const ChatSupportModal = observer(function ChatSupportModal() {
// store hooks
const { data: user } = useUser();
const { config } = useInstance();
// derived values
const intercomAppId = config?.intercom_app_id;
const isEnabled = Boolean(user && config?.is_intercom_enabled && intercomAppId);
useEffect(() => {
if (!isEnabled || !user || !intercomAppId) return;
Intercom({
app_id: intercomAppId,
user_id: user.id,
name: `${user.first_name} ${user.last_name}`,
email: user.email,
hide_default_launcher: true,
});
const handleOpenChatSupport = () => {
show();
};
window.addEventListener(CHAT_SUPPORT_EVENTS.open, handleOpenChatSupport);
return () => {
window.removeEventListener(CHAT_SUPPORT_EVENTS.open, handleOpenChatSupport);
shutdown();
};
}, [user, intercomAppId, isEnabled]);
return null;
});
export default ChatSupportModal;

View file

@ -5,7 +5,7 @@ import { DiscordIcon } from "@plane/propel/icons";
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// hooks
import { usePowerK } from "@/hooks/store/use-power-k";
import { useTransient } from "@/hooks/store/use-transient";
import { useChatSupport } from "@/hooks/use-chat-support";
/**
* Help commands - Help related commands
@ -13,7 +13,7 @@ import { useTransient } from "@/hooks/store/use-transient";
export const usePowerKHelpCommands = (): TPowerKCommandConfig[] => {
// store
const { toggleShortcutsListModal } = usePowerK();
const { toggleIntercom } = useTransient();
const { isEnabled: isChatSupportEnabled, openChatSupport } = useChatSupport();
return [
{
@ -73,9 +73,9 @@ export const usePowerKHelpCommands = (): TPowerKCommandConfig[] => {
group: "help",
i18n_title: "power_k.help_actions.chat_with_us",
icon: MessageSquare,
action: () => toggleIntercom(true),
isEnabled: () => true,
isVisible: () => true,
action: () => openChatSupport(),
isEnabled: () => isChatSupportEnabled,
isVisible: () => isChatSupportEnabled,
closeOnSelect: true,
},
];

View file

@ -1,125 +0,0 @@
import React, { useState } from "react";
import { observer } from "mobx-react";
import { HelpCircle, MessagesSquare, User } from "lucide-react";
import { useTranslation } from "@plane/i18n";
import { PageIcon } from "@plane/propel/icons";
// ui
import { Tooltip } from "@plane/propel/tooltip";
import { CustomMenu } from "@plane/ui";
// components
import { cn } from "@plane/utils";
import { ProductUpdatesModal } from "@/components/global";
// helpers
// hooks
import { useInstance } from "@/hooks/store/use-instance";
import { usePowerK } from "@/hooks/store/use-power-k";
import { useTransient } from "@/hooks/store/use-transient";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { PlaneVersionNumber } from "@/plane-web/components/global";
export interface WorkspaceHelpSectionProps {
setSidebarActive?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const HelpMenu = observer(function HelpMenu(_props: WorkspaceHelpSectionProps) {
// store hooks
const { t } = useTranslation();
const { toggleShortcutsListModal } = usePowerK();
const { isMobile } = usePlatformOS();
const { config } = useInstance();
const { isIntercomToggle, toggleIntercom } = useTransient();
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
const [isProductUpdatesModalOpen, setProductUpdatesModalOpen] = useState(false);
const handleCrispWindowShow = () => {
toggleIntercom(!isIntercomToggle);
};
return (
<>
<ProductUpdatesModal isOpen={isProductUpdatesModalOpen} handleClose={() => setProductUpdatesModalOpen(false)} />
<div className="relative flex flex-shrink-0 items-center gap-1 justify-evenly">
<CustomMenu
customButton={
<div
className={cn(
"grid place-items-center rounded-md p-1 outline-none text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90",
{
"bg-custom-background-90": isNeedHelpOpen,
}
)}
>
<Tooltip tooltipContent="Help" isMobile={isMobile} disabled={isNeedHelpOpen}>
<HelpCircle className="h-[18px] w-[18px] outline-none" />
</Tooltip>
</div>
}
customButtonClassName="relative grid place-items-center rounded-md p-1.5 outline-none"
menuButtonOnClick={() => !isNeedHelpOpen && setIsNeedHelpOpen(true)}
onMenuClose={() => setIsNeedHelpOpen(false)}
placement="top-end"
maxHeight="lg"
closeOnSelect
>
<CustomMenu.MenuItem
onClick={() => window.open("https://go.plane.so/p-docs", "_blank", "noopener,noreferrer")}
>
<div className="flex items-center gap-x-2 rounded text-xs hover:bg-custom-background-80">
<PageIcon className="h-3.5 w-3.5 text-custom-text-200" height={14} width={14} />
<span className="text-xs">{t("documentation")}</span>
</div>
</CustomMenu.MenuItem>
{config?.intercom_app_id && config?.is_intercom_enabled && (
<CustomMenu.MenuItem>
<button
type="button"
onClick={handleCrispWindowShow}
className="flex w-full items-center gap-x-2 rounded text-xs hover:bg-custom-background-80"
>
<MessagesSquare className="h-3.5 w-3.5 text-custom-text-200" />
<span className="text-xs">{t("message_support")}</span>
</button>
</CustomMenu.MenuItem>
)}
<CustomMenu.MenuItem onClick={() => window.open("mailto:sales@plane.so", "_blank", "noopener,noreferrer")}>
<div className="flex items-center gap-x-2 rounded text-xs hover:bg-custom-background-80">
<User className="h-3.5 w-3.5 text-custom-text-200" size={14} />
<span className="text-xs">{t("contact_sales")}</span>
</div>
</CustomMenu.MenuItem>
<div className="my-1 border-t border-custom-border-200" />
<CustomMenu.MenuItem>
<button
type="button"
onClick={() => toggleShortcutsListModal(true)}
className="flex w-full items-center justify-start text-xs hover:bg-custom-background-80"
>
<span className="text-xs">{t("keyboard_shortcuts")}</span>
</button>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem>
<button
type="button"
onClick={() => setProductUpdatesModalOpen(true)}
className="flex w-full items-center justify-start text-xs hover:bg-custom-background-80"
>
<span className="text-xs">{t("whats_new")}</span>
</button>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem
onClick={() => window.open("https://go.plane.so/p-discord", "_blank", "noopener,noreferrer")}
>
<div className="flex items-center gap-x-2 rounded text-xs hover:bg-custom-background-80">
<span className="text-xs">Discord</span>
</div>
</CustomMenu.MenuItem>
<div className="px-1 pt-2 mt-1 text-xs text-custom-text-200 border-t border-custom-border-200">
<PlaneVersionNumber />
</div>
</CustomMenu>
</div>
</>
);
});

View file

@ -1,147 +0,0 @@
import React, { useState } from "react";
import { observer } from "mobx-react";
import { HelpCircle, MessagesSquare, MoveLeft, User } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n";
import { PageIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { CustomMenu } from "@plane/ui";
import { cn } from "@plane/utils";
// components
import { ProductUpdatesModal } from "@/components/global";
// hooks
import { useAppTheme } from "@/hooks/store/use-app-theme";
import { useInstance } from "@/hooks/store/use-instance";
import { usePowerK } from "@/hooks/store/use-power-k";
import { useTransient } from "@/hooks/store/use-transient";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { PlaneVersionNumber } from "@/plane-web/components/global";
import { WorkspaceEditionBadge } from "@/plane-web/components/workspace/edition-badge";
export interface WorkspaceHelpSectionProps {
setSidebarActive?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const SidebarHelpSection = observer(function SidebarHelpSection(_props: WorkspaceHelpSectionProps) {
// store hooks
const { t } = useTranslation();
const { sidebarCollapsed: isCollapsed, toggleSidebar, sidebarPeek, toggleSidebarPeek } = useAppTheme();
const { toggleShortcutsListModal } = usePowerK();
const { isMobile } = usePlatformOS();
const { config } = useInstance();
const { isIntercomToggle, toggleIntercom } = useTransient();
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
const [isProductUpdatesModalOpen, setProductUpdatesModalOpen] = useState(false);
const handleCrispWindowShow = () => {
toggleIntercom(!isIntercomToggle);
};
return (
<>
<ProductUpdatesModal isOpen={isProductUpdatesModalOpen} handleClose={() => setProductUpdatesModalOpen(false)} />
<div className="flex w-full items-center justify-between px-2 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 h-12 flex-shrink-0">
<div className="relative flex flex-shrink-0 items-center gap-1 justify-evenly">
<CustomMenu
customButton={
<div
className={cn(
"grid place-items-center rounded-md p-1 outline-none text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90",
{
"bg-custom-background-90": isNeedHelpOpen,
}
)}
>
<Tooltip tooltipContent="Help" isMobile={isMobile} disabled={isNeedHelpOpen}>
<HelpCircle className="h-[18px] w-[18px] outline-none" />
</Tooltip>
</div>
}
customButtonClassName="relative grid place-items-center rounded-md p-1.5 outline-none"
menuButtonOnClick={() => !isNeedHelpOpen && setIsNeedHelpOpen(true)}
onMenuClose={() => setIsNeedHelpOpen(false)}
placement="top-end"
maxHeight="lg"
closeOnSelect
>
<CustomMenu.MenuItem onClick={() => window.open("https://go.plane.so/p-docs", "_blank")}>
<div className="flex items-center gap-x-2 rounded text-xs">
<PageIcon className="h-3.5 w-3.5 text-custom-text-200" height={14} width={14} />
<span className="text-xs">{t("documentation")}</span>
</div>
</CustomMenu.MenuItem>
{config?.intercom_app_id && config?.is_intercom_enabled && (
<CustomMenu.MenuItem>
<button
type="button"
onClick={handleCrispWindowShow}
className="flex w-full items-center gap-x-2 rounded text-xs hover:bg-custom-background-80"
>
<MessagesSquare className="h-3.5 w-3.5 text-custom-text-200" />
<span className="text-xs">{t("message_support")}</span>
</button>
</CustomMenu.MenuItem>
)}
<CustomMenu.MenuItem onClick={() => window.open("mailto:sales@plane.so", "_blank")}>
<div className="flex items-center gap-x-2 rounded text-xs">
<User className="h-3.5 w-3.5 text-custom-text-200" size={14} />
<span className="text-xs">{t("contact_sales")}</span>
</div>
</CustomMenu.MenuItem>
<div className="my-1 border-t border-custom-border-200" />
<CustomMenu.MenuItem>
<button
type="button"
onClick={() => toggleShortcutsListModal(true)}
className="flex w-full items-center justify-start text-xs hover:bg-custom-background-80"
>
<span className="text-xs">{t("keyboard_shortcuts")}</span>
</button>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem>
<button
type="button"
onClick={() => setProductUpdatesModalOpen(true)}
className="flex w-full items-center justify-start text-xs hover:bg-custom-background-80"
>
<span className="text-xs">{t("whats_new")}</span>
</button>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => window.open("https://go.plane.so/p-discord", "_blank")}>
<div className="flex items-center gap-x-2 rounded text-xs">
<span className="text-xs">Discord</span>
</div>
</CustomMenu.MenuItem>
<div className="px-1 pt-2 mt-1 text-xs text-custom-text-200 border-t border-custom-border-200">
<PlaneVersionNumber />
</div>
</CustomMenu>
</div>
<div className="w-full flex-grow px-0.5">
<WorkspaceEditionBadge />
</div>
<div className="flex flex-shrink-0 items-center gap-1 justify-evenly">
<Tooltip tooltipContent={`${isCollapsed ? "Expand" : "Hide"}`} isMobile={isMobile}>
<button
type="button"
className="grid place-items-center rounded-md p-1 text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100"
onClick={() => {
if (sidebarPeek) toggleSidebarPeek(false);
toggleSidebar();
}}
aria-label={t(
isCollapsed
? "aria_labels.projects_sidebar.expand_sidebar"
: "aria_labels.projects_sidebar.collapse_sidebar"
)}
>
<MoveLeft className={`size-4 duration-300 ${isCollapsed ? "rotate-180" : ""}`} />
</button>
</Tooltip>
</div>
</div>
</>
);
});

View file

@ -9,9 +9,8 @@ import { CustomMenu } from "@plane/ui";
import { ProductUpdatesModal } from "@/components/global";
import { AppSidebarItem } from "@/components/sidebar/sidebar-item";
// hooks
import { useInstance } from "@/hooks/store/use-instance";
import { usePowerK } from "@/hooks/store/use-power-k";
import { useTransient } from "@/hooks/store/use-transient";
import { useChatSupport } from "@/hooks/use-chat-support";
// plane web components
import { PlaneVersionNumber } from "@/plane-web/components/global";
@ -19,16 +18,11 @@ export const HelpMenuRoot = observer(function HelpMenuRoot() {
// store hooks
const { t } = useTranslation();
const { toggleShortcutsListModal } = usePowerK();
const { config } = useInstance();
const { isIntercomToggle, toggleIntercom } = useTransient();
const { openChatSupport, isEnabled: isChatSupportEnabled } = useChatSupport();
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
const [isProductUpdatesModalOpen, setProductUpdatesModalOpen] = useState(false);
const handleCrispWindowShow = () => {
toggleIntercom(!isIntercomToggle);
};
return (
<>
<ProductUpdatesModal isOpen={isProductUpdatesModalOpen} handleClose={() => setProductUpdatesModalOpen(false)} />
@ -38,7 +32,7 @@ export const HelpMenuRoot = observer(function HelpMenuRoot() {
<AppSidebarItem
variant="button"
item={{
icon: <HelpCircle className="size-5" />,
icon: <HelpCircle className="size-4" />,
isActive: isNeedHelpOpen,
}}
/>
@ -56,11 +50,11 @@ export const HelpMenuRoot = observer(function HelpMenuRoot() {
<span className="text-xs">{t("documentation")}</span>
</div>
</CustomMenu.MenuItem>
{config?.intercom_app_id && config?.is_intercom_enabled && (
{isChatSupportEnabled && (
<CustomMenu.MenuItem>
<button
type="button"
onClick={handleCrispWindowShow}
onClick={openChatSupport}
className="flex w-full items-center gap-x-2 rounded text-xs hover:bg-custom-background-80"
>
<MessagesSquare className="h-3.5 w-3.5 text-custom-text-200" />