[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:
parent
e650b19933
commit
cacd1b489e
12 changed files with 97 additions and 394 deletions
|
|
@ -25,8 +25,8 @@ const PostHogProvider = lazy(function PostHogProvider() {
|
|||
return import("@/lib/posthog-provider");
|
||||
});
|
||||
|
||||
const IntercomProvider = lazy(function IntercomProvider() {
|
||||
return import("@/lib/intercom-provider");
|
||||
const ChatSupportModal = lazy(function ChatSupportModal() {
|
||||
return import("@/components/global/chat-support-modal");
|
||||
});
|
||||
|
||||
export interface IAppProvider {
|
||||
|
|
@ -50,11 +50,10 @@ export function AppProvider(props: IAppProvider) {
|
|||
<StoreWrapper>
|
||||
<InstanceWrapper>
|
||||
<Suspense>
|
||||
<IntercomProvider>
|
||||
<ChatSupportModal />
|
||||
<PostHogProvider>
|
||||
<SWRConfig value={WEB_SWR_CONFIG}>{children}</SWRConfig>
|
||||
</PostHogProvider>
|
||||
</IntercomProvider>
|
||||
</Suspense>
|
||||
</InstanceWrapper>
|
||||
</StoreWrapper>
|
||||
|
|
|
|||
43
apps/web/core/components/global/chat-support-modal.tsx
Normal file
43
apps/web/core/components/global/chat-support-modal.tsx
Normal 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;
|
||||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -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" />
|
||||
|
|
|
|||
13
apps/web/core/custom-events/chat-support.ts
Normal file
13
apps/web/core/custom-events/chat-support.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
type ChatSupportType = "open";
|
||||
|
||||
type ChatSupportEventType = `chat-support:${ChatSupportType}`;
|
||||
|
||||
export const CHAT_SUPPORT_EVENTS = {
|
||||
open: "chat-support:open",
|
||||
} satisfies Record<ChatSupportType, ChatSupportEventType>;
|
||||
|
||||
export class ChatSupportEvent extends CustomEvent<ChatSupportType> {
|
||||
constructor(type: ChatSupportType) {
|
||||
super(CHAT_SUPPORT_EVENTS[type]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { useContext } from "react";
|
||||
// mobx store
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
// types
|
||||
import type { ITransientStore } from "@/store/transient.store";
|
||||
|
||||
export const useTransient = (): ITransientStore => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useTransient must be used within StoreProvider");
|
||||
return context.transient;
|
||||
};
|
||||
25
apps/web/core/hooks/use-chat-support.ts
Normal file
25
apps/web/core/hooks/use-chat-support.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { useCallback } from "react";
|
||||
// custom events
|
||||
import { ChatSupportEvent } from "@/custom-events/chat-support";
|
||||
// hooks
|
||||
import { useInstance } from "@/hooks/store/use-instance";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
|
||||
export interface IUseChatSupport {
|
||||
openChatSupport: () => void;
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
export const useChatSupport = (): IUseChatSupport => {
|
||||
const { data: user } = useUser();
|
||||
const { config } = useInstance();
|
||||
// derived values
|
||||
const isEnabled = Boolean(user && config?.is_intercom_enabled && config?.intercom_app_id);
|
||||
|
||||
const openChatSupport = useCallback(() => {
|
||||
if (!isEnabled) return;
|
||||
window.dispatchEvent(new ChatSupportEvent("open"));
|
||||
}, [isEnabled]);
|
||||
|
||||
return { openChatSupport, isEnabled };
|
||||
};
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Intercom, show, hide, onHide } from "@intercom/messenger-js-sdk";
|
||||
import { observer } from "mobx-react";
|
||||
// store hooks
|
||||
import { useInstance } from "@/hooks/store/use-instance";
|
||||
import { useTransient } from "@/hooks/store/use-transient";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
|
||||
export type IntercomProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const IntercomProvider = observer(function IntercomProvider(props: IntercomProviderProps) {
|
||||
const { children } = props;
|
||||
// hooks
|
||||
const { data: user } = useUser();
|
||||
const { config } = useInstance();
|
||||
const { isIntercomToggle, toggleIntercom } = useTransient();
|
||||
// refs
|
||||
const isInitializedRef = useRef(false);
|
||||
// states
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
// derived values
|
||||
const isIntercomEnabled = user && config && config.is_intercom_enabled && config.intercom_app_id;
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydrated) return;
|
||||
if (isIntercomToggle) show();
|
||||
else hide();
|
||||
}, [hydrated, isIntercomToggle]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydrated) return;
|
||||
onHide(() => {
|
||||
toggleIntercom(false);
|
||||
});
|
||||
}, [hydrated, toggleIntercom]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isIntercomEnabled || isInitializedRef.current) return; // prevent multiple initializations
|
||||
Intercom({
|
||||
app_id: config.intercom_app_id || "",
|
||||
user_id: user.id,
|
||||
name: `${user.first_name} ${user.last_name}`,
|
||||
email: user.email,
|
||||
hide_default_launcher: true,
|
||||
});
|
||||
isInitializedRef.current = true;
|
||||
setHydrated(true);
|
||||
}, [isIntercomEnabled, config, user]);
|
||||
|
||||
return <>{children}</>;
|
||||
});
|
||||
|
||||
export default IntercomProvider;
|
||||
|
|
@ -58,8 +58,6 @@ import type { IStickyStore } from "./sticky/sticky.store";
|
|||
import { StickyStore } from "./sticky/sticky.store";
|
||||
import type { IThemeStore } from "./theme.store";
|
||||
import { ThemeStore } from "./theme.store";
|
||||
import type { ITransientStore } from "./transient.store";
|
||||
import { TransientStore } from "./transient.store";
|
||||
import type { IUserStore } from "./user";
|
||||
import { UserStore } from "./user";
|
||||
import type { IWorkspaceRootStore } from "./workspace";
|
||||
|
|
@ -93,7 +91,6 @@ export class CoreRootStore {
|
|||
multipleSelect: IMultipleSelectStore;
|
||||
workspaceNotification: IWorkspaceNotificationStore;
|
||||
favorite: IFavoriteStore;
|
||||
transient: ITransientStore;
|
||||
stickyStore: IStickyStore;
|
||||
editorAssetStore: IEditorAssetStore;
|
||||
workItemFilters: IWorkItemFilterStore;
|
||||
|
|
@ -124,7 +121,6 @@ export class CoreRootStore {
|
|||
this.projectEstimate = new ProjectEstimateStore(this);
|
||||
this.workspaceNotification = new WorkspaceNotificationStore(this);
|
||||
this.favorite = new FavoriteStore(this);
|
||||
this.transient = new TransientStore();
|
||||
this.stickyStore = new StickyStore();
|
||||
this.editorAssetStore = new EditorAssetStore();
|
||||
this.analytics = new AnalyticsStore();
|
||||
|
|
@ -159,7 +155,6 @@ export class CoreRootStore {
|
|||
this.projectEstimate = new ProjectEstimateStore(this);
|
||||
this.workspaceNotification = new WorkspaceNotificationStore(this);
|
||||
this.favorite = new FavoriteStore(this);
|
||||
this.transient = new TransientStore();
|
||||
this.stickyStore = new StickyStore();
|
||||
this.editorAssetStore = new EditorAssetStore();
|
||||
this.workItemFilters = new WorkItemFilterStore();
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
import { action, observable, makeObservable } from "mobx";
|
||||
|
||||
export interface ITransientStore {
|
||||
// observables
|
||||
isIntercomToggle: boolean;
|
||||
// actions
|
||||
toggleIntercom: (intercomToggle: boolean) => void;
|
||||
}
|
||||
|
||||
export class TransientStore implements ITransientStore {
|
||||
// observables
|
||||
isIntercomToggle: boolean = false;
|
||||
|
||||
constructor() {
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
isIntercomToggle: observable.ref,
|
||||
// action
|
||||
toggleIntercom: action,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Toggle the intercom collapsed state
|
||||
* @param { boolean } intercomToggle
|
||||
*/
|
||||
toggleIntercom = (intercomToggle: boolean) => (this.isIntercomToggle = intercomToggle);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue