🔥(frontend) remove Crisp from the project

Remove Crisp chat SDK, provider, analytics, session management
and all related references across the codebase.
This commit is contained in:
Cyril
2026-06-23 13:37:50 +02:00
parent 31749b3051
commit b9e4df7bd6
16 changed files with 2 additions and 224 deletions
+1
View File
@@ -20,6 +20,7 @@ and this project adheres to
- ♿️(frontend) hide mobile left panel from screen readers when collapsed #2450
- ♿️(frontend) enable blocknote heading ids for toc anchors #2449
- ♿️(frontend) focus export modal on format select #2421
- ♿️(frontend) configurable legal submenu in HelpMenu, remove Crisp #2416
## [v5.3.0] - 2026-06-19
-1
View File
@@ -44,7 +44,6 @@ These are the environment variables you can set for the `impress-backend` contai
| CONVERSION_FILE_MAX_SIZE | The file max size allowed when uploaded to convert it | 20971520 (20MB) |
| CONVERSION_FILE_EXTENSIONS_ALLOWED | Extension list managed by the conversion service | [".docx", ".md"] |
| CONVERSION_UPLOAD_ENABLED | Enable or not the conversion feature. Without it the endpoint will fail and the logo in the front application will not appear | False |
| CRISP_WEBSITE_ID | Crisp website id for support | |
| DB_ENGINE | Engine to use for database connections | django.db.backends.postgresql_psycopg2 |
| DB_HOST | Host of the database | localhost |
| DB_NAME | Name of the database | impress |
-1
View File
@@ -3076,7 +3076,6 @@ class ConfigView(drf.views.APIView):
"CONVERSION_FILE_EXTENSIONS_ALLOWED",
"CONVERSION_FILE_MAX_SIZE",
"CONVERSION_UPLOAD_ENABLED",
"CRISP_WEBSITE_ID",
"ENVIRONMENT",
"FRONTEND_CSS_URL",
"FRONTEND_HOMEPAGE_FEATURE_ENABLED",
@@ -28,7 +28,6 @@ pytestmark = pytest.mark.django_db
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=True,
COLLABORATION_WS_INACTIVITY_TIMEOUT=300,
CONVERSION_UPLOAD_ENABLED=False,
CRISP_WEBSITE_ID="123",
FRONTEND_CSS_URL="http://testcss/",
FRONTEND_JS_URL="http://testjs/",
FRONTEND_THEME="test-theme",
@@ -62,7 +61,6 @@ def test_api_config(is_authenticated):
"CONVERSION_FILE_EXTENSIONS_ALLOWED": [".docx", ".md"],
"CONVERSION_FILE_MAX_SIZE": 20971520,
"CONVERSION_UPLOAD_ENABLED": False,
"CRISP_WEBSITE_ID": "123",
"ENVIRONMENT": "test",
"FRONTEND_CSS_URL": "http://testcss/",
"FRONTEND_HOMEPAGE_FEATURE_ENABLED": True,
-5
View File
@@ -569,11 +569,6 @@ class Base(Configuration):
"https://eu.i.posthog.com", environ_name="POSTHOG_HOST", environ_prefix=None
)
# Crisp
CRISP_WEBSITE_ID = values.Value(
None, environ_name="CRISP_WEBSITE_ID", environ_prefix=None
)
# Easy thumbnails
THUMBNAIL_EXTENSION = "webp"
THUMBNAIL_TRANSPARENCY_EXTENSION = "webp"
@@ -80,61 +80,6 @@ test.describe('Help feature', () => {
});
});
test.describe('Support button', () => {
if (process.env.IS_INSTANCE !== 'true') {
test('is not displayed if CRISP_WEBSITE_ID is not set', async ({
page,
}) => {
await overrideConfig(page, {
CRISP_WEBSITE_ID: '',
});
await page.goto('/');
await page.getByRole('button', { name: 'Open help menu' }).click();
await expect(
page.getByRole('menuitem', { name: 'Get Support' }),
).toBeHidden();
});
test('is displayed if CRISP_WEBSITE_ID is set', async ({ page }) => {
await overrideConfig(page, {
CRISP_WEBSITE_ID: 'test_website_id',
});
await page.goto('/');
await page.getByRole('button', { name: 'Open help menu' }).click();
await expect(
page.getByRole('menuitem', {
name: 'Get Support',
}),
).toBeVisible();
});
}
if (process.env.IS_INSTANCE === 'true') {
test('it displays Crisp chatbox', async ({ page }) => {
const currentConfig = await getCurrentConfig(page);
test.skip(
!currentConfig.CRISP_WEBSITE_ID,
'Crisp chatbox is not enabled',
);
await page.goto('/');
await page.getByRole('button', { name: 'Open help menu' }).click();
await page
.getByRole('menuitem', {
name: 'Get Support',
})
.click();
const crispElement = page.locator('#crisp-chatbox');
await expect(crispElement).toBeAttached();
});
}
});
test.describe('Legal submenu', () => {
if (process.env.IS_INSTANCE !== 'true') {
test('is not displayed if legal_links are not set', async ({ page }) => {
@@ -18,7 +18,6 @@ export const CONFIG = {
AI_FEATURE_BLOCKNOTE_ENABLED: false,
AI_FEATURE_LEGACY_ENABLED: true,
API_USERS_SEARCH_QUERY_MIN_LENGTH: 3,
CRISP_WEBSITE_ID: null,
COLLABORATION_WS_INACTIVITY_TIMEOUT: 15,
COLLABORATION_WS_URL: process.env.COLLABORATION_WS_URL,
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
-1
View File
@@ -55,7 +55,6 @@
"canvg": "4.0.3",
"clsx": "2.1.1",
"cmdk": "1.1.1",
"crisp-sdk-web": "1.1.2",
"emoji-datasource-apple": "16.0.0",
"emoji-mart": "5.6.0",
"emoji-regex": "10.6.0",
@@ -77,16 +77,6 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
});
}, [conf?.POSTHOG_KEY, conf?.POSTHOG_HOST]);
useEffect(() => {
if (!conf?.CRISP_WEBSITE_ID) {
return;
}
void import('@/services').then(({ CrispAnalytic }) => {
new CrispAnalytic({ websiteId: conf.CRISP_WEBSITE_ID });
});
}, [conf?.CRISP_WEBSITE_ID]);
useEffect(() => {
const frontendVersion = process.env.NEXT_PUBLIC_APP_VERSION;
@@ -52,7 +52,6 @@ export interface ConfigResponse {
CONVERSION_FILE_EXTENSIONS_ALLOWED: string[];
CONVERSION_FILE_MAX_SIZE: number;
CONVERSION_UPLOAD_ENABLED?: boolean;
CRISP_WEBSITE_ID?: string;
ENVIRONMENT: string;
FRONTEND_CSS_URL?: string;
FRONTEND_HOMEPAGE_FEATURE_ENABLED?: boolean;
@@ -4,11 +4,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
import { SILENT_LOGIN_RETRY } from '../conf';
import { gotoLogout, gotoSilentLogin } from '../utils';
// Mock the Crisp service
vi.mock('@/services/Crisp', () => ({
terminateCrispSession: vi.fn(),
}));
// Add mock on window.location.replace
const mockReplace = vi.fn();
Object.defineProperty(window, 'location', {
@@ -32,12 +27,9 @@ describe('utils', () => {
localStorage.clear();
});
it('checks support session is terminated when logout', async () => {
const { terminateCrispSession } = await import('@/services/Crisp');
it('checks logout redirects to logout URL', async () => {
gotoLogout();
expect(terminateCrispSession).toHaveBeenCalled();
expect(mockReplace).toHaveBeenCalledWith(
'http://test.jest/api/v1.0/logout/',
);
@@ -1,4 +1,3 @@
import { terminateCrispSession } from '@/services/Crisp';
import { safeLocalStorage, safeSessionStorage } from '@/utils/storages';
import {
@@ -66,6 +65,5 @@ export const resetSilent = () => {
};
export const gotoLogout = () => {
terminateCrispSession();
window.location.replace(LOGOUT_URL);
};
@@ -8,15 +8,12 @@ import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { createGlobalStyle, css } from 'styled-components';
import BubbleTextIcon from '@/assets/icons/ui-kit/bubble-text.svg';
import DocIcon from '@/assets/icons/ui-kit/doc.svg';
import LegalIcon from '@/assets/icons/ui-kit/legal.svg';
import HelpIcon from '@/assets/icons/ui-kit/question-mark.svg';
import WandAndStarsIcon from '@/assets/icons/ui-kit/wand-and-stars.svg';
import { Box } from '@/components';
import { useConfig } from '@/core';
import { openCrispChat } from '@/services';
import { OnBoarding } from './OnBoarding';
const HelpMenuStyle = createGlobalStyle`
@@ -36,7 +33,6 @@ export const HelpMenu = ({
const { data: config } = useConfig();
const onboardingEnabled = config?.theme_customization?.onboarding?.enabled;
const documentationUrl = config?.theme_customization?.help?.documentation_url;
const crispEnabled = !!config?.CRISP_WEBSITE_ID;
const legalLinks = config?.theme_customization?.help?.legal_links;
const toggleMenu = useCallback(() => {
@@ -55,12 +51,6 @@ export const HelpMenu = ({
const options = useMemo<DropdownMenuItem[]>(
() => [
{
label: t('Get Support'),
icon: <BubbleTextIcon aria-hidden="true" width="24" height="24" />,
callback: openCrispChat,
isHidden: !crispEnabled,
},
{
label: t('Documentation'),
icon: <DocIcon aria-hidden="true" width="24" height="24" />,
@@ -108,7 +98,6 @@ export const HelpMenu = ({
],
[
t,
crispEnabled,
documentationUrl,
modalOnbording.open,
onboardingEnabled,
@@ -37,7 +37,6 @@ export const LeftPanelDesktop = () => {
const { data: config } = useConfig();
const showHelpMenu =
config?.theme_customization?.onboarding?.enabled ||
!!config?.CRISP_WEBSITE_ID ||
!!config?.theme_customization?.help?.documentation_url ||
!!config?.theme_customization?.help?.legal_links;
@@ -1,123 +0,0 @@
/**
* Configure Crisp chat for real-time support across all pages.
*/
import { ChatboxPosition, Crisp } from 'crisp-sdk-web';
import { JSX, PropsWithChildren, ReactNode, useEffect, useState } from 'react';
import { createGlobalStyle } from 'styled-components';
import { User } from '@/features/auth';
import { AbstractAnalytic, AnalyticEvent } from '@/libs';
const CrispStyle = createGlobalStyle`
#crisp-chatbox div[role="button"] {
zoom: 0.7;
right: auto !important;
left: 24px !important;
}
#crisp-chatbox div[data-chat-status="initial"] {
bottom: 65px!important;
left: 24px !important;
margin-left: var(--crisp-customization-button-horizontal) !important;
right: auto !important;
}
`;
export const initializeCrispSession = (user: User) => {
if (!Crisp.isCrispInjected()) {
return;
}
Crisp.setTokenId(`impress-${user.id}`);
Crisp.user.setEmail(user.email);
};
export const configureCrispSession = (websiteId: string) => {
if (Crisp.isCrispInjected()) {
return;
}
Crisp.configure(websiteId);
Crisp.setSafeMode(true);
Crisp.setPosition(ChatboxPosition.Left);
Crisp.chat.hide();
Crisp.chat.onChatClosed(() => {
Crisp.chat.hide();
});
};
export const openCrispChat = () => {
if (!Crisp.isCrispInjected()) {
return;
}
Crisp.setPosition(ChatboxPosition.Left);
Crisp.chat.show();
setTimeout(() => {
Crisp.chat.open();
}, 300);
};
export const terminateCrispSession = () => {
if (!Crisp.isCrispInjected()) {
return;
}
Crisp.setTokenId();
Crisp.session.reset();
};
interface CrispProviderProps {
websiteId?: string;
}
export const CrispProvider = ({
children,
websiteId,
}: PropsWithChildren<CrispProviderProps>) => {
const [isConfigured, setIsConfigured] = useState(false);
useEffect(() => {
if (!websiteId) {
return;
}
setIsConfigured(true);
configureCrispSession(websiteId);
}, [websiteId]);
return (
<>
{isConfigured && <CrispStyle />}
{children}
</>
);
};
export class CrispAnalytic extends AbstractAnalytic {
private conf?: CrispProviderProps = undefined;
private EVENT = {
PUBLIC_DOC_NOT_CONNECTED: 'public-doc-not-connected',
};
public constructor(conf?: CrispProviderProps) {
super();
this.conf = conf;
}
public Provider(children?: ReactNode): JSX.Element {
return (
<CrispProvider websiteId={this.conf?.websiteId}>{children}</CrispProvider>
);
}
public trackEvent(evt: AnalyticEvent): void {
if (evt.eventName === 'doc') {
if (evt.isPublic && !evt.authenticated) {
Crisp.trigger.run(this.EVENT.PUBLIC_DOC_NOT_CONNECTED);
}
}
}
public isFeatureFlagActivated(): boolean {
return true;
}
}
@@ -1,2 +1 @@
export * from './Crisp';
export * from './PosthogAnalytic';