diff --git a/package.json b/package.json
index 173cf77565..2645e62ce3 100644
--- a/package.json
+++ b/package.json
@@ -266,7 +266,7 @@
"@lobechat/ssrf-safe-fetch": "workspace:*",
"@lobechat/utils": "workspace:*",
"@lobechat/web-crawler": "workspace:*",
- "@lobehub/analytics": "^1.6.0",
+ "@lobehub/analytics": "^1.6.2",
"@lobehub/charts": "^5.0.0",
"@lobehub/desktop-ipc-typings": "workspace:*",
"@lobehub/editor": "^4.8.1",
diff --git a/src/app/[variants]/(auth)/_layout/AuthGlobalProvider.tsx b/src/app/[variants]/(auth)/_layout/AuthGlobalProvider.tsx
index 531e0dfb09..78ee31b447 100644
--- a/src/app/[variants]/(auth)/_layout/AuthGlobalProvider.tsx
+++ b/src/app/[variants]/(auth)/_layout/AuthGlobalProvider.tsx
@@ -1,6 +1,7 @@
import { type ReactNode } from 'react';
import { appEnv } from '@/envs/app';
+import AnalyticsRSCProvider from '@/layout/AnalyticsRSCProvider';
import AuthProvider from '@/layout/AuthProvider';
import NextThemeProvider from '@/layout/GlobalProvider/NextThemeProvider';
import StyleRegistry from '@/layout/GlobalProvider/StyleRegistry';
@@ -30,7 +31,9 @@ const AuthGlobalProvider = async ({ children, variants }: AuthGlobalProviderProp
segmentVariants={variants}
serverConfig={serverConfig}
>
- {children}
+
+ {children}
+
diff --git a/src/app/[variants]/(auth)/signin/useSignIn.ts b/src/app/[variants]/(auth)/signin/useSignIn.ts
index 8aea1e23d1..482cd669f0 100644
--- a/src/app/[variants]/(auth)/signin/useSignIn.ts
+++ b/src/app/[variants]/(auth)/signin/useSignIn.ts
@@ -8,6 +8,7 @@ import { type CheckUserResponseData } from '@/app/(backend)/api/auth/check-user/
import { type ResolveUsernameResponseData } from '@/app/(backend)/api/auth/resolve-username/route';
import { useBusinessSignin } from '@/business/client/hooks/useBusinessSignin';
import { message } from '@/components/AntdStaticMethods';
+import { trackLoginOrSignupClicked } from '@/features/User/UserLoginOrSignup/trackLoginOrSignupClicked';
import { requestPasswordReset, signIn } from '@/libs/better-auth/auth-client';
import { isBuiltinProvider, normalizeProviderId } from '@/libs/better-auth/utils/client';
@@ -125,6 +126,8 @@ export const useSignIn = () => {
const handleCheckUser = async (values: Pick) => {
setLoading(true);
+ await trackLoginOrSignupClicked({ spm: 'signin.email_step.submit' });
+
try {
const resolvedEmail = await resolveEmailFromIdentifier(values.email);
if (!resolvedEmail) return;
@@ -172,6 +175,8 @@ export const useSignIn = () => {
const handleSignIn = async (values: Pick) => {
setLoading(true);
+ await trackLoginOrSignupClicked({ spm: 'signin.password_step.submit' });
+
try {
const callbackUrl = searchParams.get('callbackUrl') || '/';
const result = await signIn.email(
@@ -203,6 +208,11 @@ export const useSignIn = () => {
const handleSocialSignIn = async (provider: string) => {
setSocialLoading(provider);
const normalizedProvider = normalizeProviderId(provider);
+ await trackLoginOrSignupClicked({
+ provider: normalizedProvider,
+ spm: 'signin.social.click',
+ });
+
try {
if (ENABLE_BUSINESS_FEATURES && !(await preSocialSigninCheck())) {
setSocialLoading(null);
@@ -252,7 +262,9 @@ export const useSignIn = () => {
const params = new URLSearchParams();
if (currentEmail) params.set('email', currentEmail);
params.set('callbackUrl', callbackUrl);
- router.push(`/signup?${params.toString()}`);
+ void trackLoginOrSignupClicked({ spm: 'signin.go_to_signup.click' }).finally(() => {
+ router.push(`/signup?${params.toString()}`);
+ });
};
const handleForgotPassword = async () => {
diff --git a/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx b/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx
index fbf35c3038..e065a1881b 100644
--- a/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx
+++ b/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx
@@ -9,6 +9,7 @@ import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { AuthCard } from '../../../../../features/AuthCard';
+import { trackLoginOrSignupClicked } from '../../../../../features/User/UserLoginOrSignup/trackLoginOrSignupClicked';
import { type SignUpFormValues } from './useSignUp';
import { useSignUp } from './useSignUp';
@@ -27,7 +28,17 @@ const BetterAuthSignUpForm = () => {
const footer = (
{t('betterAuth.signup.hasAccount')}{' '}
- {t('betterAuth.signup.signinLink')}
+ {
+ event.preventDefault();
+ void trackLoginOrSignupClicked({ spm: 'signup.go_to_signin.click' }).finally(() => {
+ window.location.href = `/signin?${searchParams.toString()}`;
+ });
+ }}
+ >
+ {t('betterAuth.signup.signinLink')}
+
);
diff --git a/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.ts b/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.ts
index 30eb9a8263..eaf6f773ac 100644
--- a/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.ts
+++ b/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.ts
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
import { type BusinessSignupFomData } from '@/business/client/hooks/useBusinessSignup';
import { useBusinessSignup } from '@/business/client/hooks/useBusinessSignup';
import { message } from '@/components/AntdStaticMethods';
+import { trackLoginOrSignupClicked } from '@/features/User/UserLoginOrSignup/trackLoginOrSignupClicked';
import { signUp } from '@/libs/better-auth/auth-client';
import { useAuthServerConfigStore } from '../../_layout/AuthServerConfigProvider';
@@ -26,6 +27,8 @@ export const useSignUp = () => {
const handleSignUp = async (values: SignUpFormValues) => {
setLoading(true);
+ await trackLoginOrSignupClicked({ spm: 'signup.submit.click' });
+
try {
if (ENABLE_BUSINESS_FEATURES && !(await preSocialSignupCheck(values))) {
setLoading(false);
diff --git a/src/app/spa/[variants]/[[...path]]/route.ts b/src/app/spa/[variants]/[[...path]]/route.ts
index e1ee4d3fe0..fae36823a0 100644
--- a/src/app/spa/[variants]/[[...path]]/route.ts
+++ b/src/app/spa/[variants]/[[...path]]/route.ts
@@ -131,6 +131,17 @@ function buildAnalyticsConfig(): AnalyticsConfig {
};
}
+ if (analyticsEnv.ENABLED_X_ADS && analyticsEnv.X_ADS_PIXEL_ID) {
+ config.xAds = {
+ eventIds: {
+ login_or_signup_clicked: analyticsEnv.X_ADS_LOGIN_OR_SIGNUP_CLICKED_EVENT_ID,
+ main_page_view: analyticsEnv.X_ADS_MAIN_PAGE_VIEW_EVENT_ID,
+ },
+ pixelId: analyticsEnv.X_ADS_PIXEL_ID,
+ purchaseEventId: analyticsEnv.X_ADS_PURCHASE_EVENT_ID,
+ };
+ }
+
if (analyticsEnv.REACT_SCAN_MONITOR_API_KEY) {
config.reactScan = { apiKey: analyticsEnv.REACT_SCAN_MONITOR_API_KEY };
}
diff --git a/src/components/Analytics/HomePageTracker.tsx b/src/components/Analytics/HomePageTracker.tsx
new file mode 100644
index 0000000000..451bbebc5a
--- /dev/null
+++ b/src/components/Analytics/HomePageTracker.tsx
@@ -0,0 +1,31 @@
+'use client';
+
+import { useAnalytics } from '@lobehub/analytics/react';
+import { memo, useEffect } from 'react';
+import { useLocation } from 'react-router-dom';
+
+const HomePageTracker = memo(() => {
+ const { analytics } = useAnalytics();
+ const { pathname } = useLocation();
+
+ useEffect(() => {
+ if (!analytics || pathname !== '/') return;
+
+ const timer = setTimeout(() => {
+ analytics.track({
+ name: 'main_page_view',
+ properties: {
+ spm: 'homepage.main_page.view',
+ },
+ });
+ }, 1000);
+
+ return () => clearTimeout(timer);
+ }, [analytics, pathname]);
+
+ return null;
+});
+
+HomePageTracker.displayName = 'HomePageTracker';
+
+export default HomePageTracker;
diff --git a/src/components/Analytics/LobeAnalyticsProvider.tsx b/src/components/Analytics/LobeAnalyticsProvider.tsx
index 32f9d94978..f4ae57ee8c 100644
--- a/src/components/Analytics/LobeAnalyticsProvider.tsx
+++ b/src/components/Analytics/LobeAnalyticsProvider.tsx
@@ -3,11 +3,12 @@
import {
type GoogleAnalyticsProviderConfig,
type PostHogProviderAnalyticsConfig,
+ type XAdsProviderAnalyticsConfig,
} from '@lobehub/analytics';
import { createSingletonAnalytics } from '@lobehub/analytics';
import { AnalyticsProvider } from '@lobehub/analytics/react';
import { type ReactNode } from 'react';
-import { memo, useMemo } from 'react';
+import { memo, useRef } from 'react';
import { BUSINESS_LINE } from '@/const/analytics';
import { isDesktop } from '@/const/version';
@@ -17,28 +18,32 @@ type Props = {
children: ReactNode;
ga4Config: GoogleAnalyticsProviderConfig;
postHogConfig: PostHogProviderAnalyticsConfig;
+ xAdsConfig: XAdsProviderAnalyticsConfig;
};
let analyticsInstance: ReturnType | null = null;
export const LobeAnalyticsProvider = memo(
- ({ children, ga4Config, postHogConfig }: Props) => {
- const analytics = useMemo(() => {
- if (analyticsInstance) {
- return analyticsInstance;
- }
+ ({ children, ga4Config, postHogConfig, xAdsConfig }: Props) => {
+ const analyticsRef = useRef | null>(null);
- analyticsInstance = createSingletonAnalytics({
- business: BUSINESS_LINE,
- debug: isDev,
- providers: {
- ga4: ga4Config,
- posthog: postHogConfig,
- },
- });
+ if (!analyticsRef.current) {
+ analyticsRef.current =
+ analyticsInstance ||
+ createSingletonAnalytics({
+ business: BUSINESS_LINE,
+ debug: isDev,
+ providers: {
+ ga4: ga4Config,
+ posthog: postHogConfig,
+ xAds: xAdsConfig,
+ },
+ });
- return analyticsInstance;
- }, []);
+ analyticsInstance = analyticsRef.current;
+ }
+
+ const analytics = analyticsRef.current;
if (!analytics) return children;
diff --git a/src/components/Analytics/LobeAnalyticsProviderWrapper.tsx b/src/components/Analytics/LobeAnalyticsProviderWrapper.tsx
index 4b449a7c4b..202176f3a8 100644
--- a/src/components/Analytics/LobeAnalyticsProviderWrapper.tsx
+++ b/src/components/Analytics/LobeAnalyticsProviderWrapper.tsx
@@ -30,6 +30,13 @@ export const LobeAnalyticsProviderWrapper = memo(({ children }) => {
key: analytics?.posthog?.key ?? '',
person_profiles: 'always',
}}
+ xAdsConfig={{
+ debug: isDev,
+ eventIds: analytics?.xAds?.eventIds,
+ enabled: !!analytics?.xAds?.pixelId,
+ pixelId: analytics?.xAds?.pixelId ?? '',
+ purchaseEventId: analytics?.xAds?.purchaseEventId,
+ }}
>
{children}
diff --git a/src/components/Analytics/MainInterfaceTracker.tsx b/src/components/Analytics/MainInterfaceTracker.tsx
deleted file mode 100644
index 800f172c32..0000000000
--- a/src/components/Analytics/MainInterfaceTracker.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-'use client';
-
-import { useAnalytics } from '@lobehub/analytics/react';
-import { memo, useCallback, useEffect } from 'react';
-
-import { getChatStoreState } from '@/store/chat';
-import { displayMessageSelectors } from '@/store/chat/selectors';
-import { useGlobalStore } from '@/store/global';
-import { systemStatusSelectors } from '@/store/global/selectors';
-import { getSessionStoreState } from '@/store/session';
-import { sessionSelectors } from '@/store/session/selectors';
-
-const MainInterfaceTracker = memo(() => {
- const { analytics } = useAnalytics();
-
- const getMainInterfaceAnalyticsData = useCallback(() => {
- const currentSession = sessionSelectors.currentSession(getSessionStoreState());
- const activeSessionId = currentSession?.id;
- const defaultSessions = sessionSelectors.defaultSessions(getSessionStoreState());
- const showRightPanel = systemStatusSelectors.showRightPanel(useGlobalStore.getState());
- const messages = displayMessageSelectors.activeDisplayMessages(getChatStoreState());
- return {
- active_assistant: activeSessionId === 'inbox' ? null : currentSession?.meta?.title || null,
- has_chat_history: messages.length > 0,
- session_id: activeSessionId ? activeSessionId : 'inbox',
- sidebar_state: showRightPanel ? 'expanded' : 'collapsed',
- visible_assistants_count: defaultSessions.length,
- };
- }, []);
-
- useEffect(() => {
- if (!analytics) return;
-
- const timer = setTimeout(() => {
- analytics.track({
- name: 'main_page_view',
- properties: {
- ...getMainInterfaceAnalyticsData(),
- spm: 'main_page.interface.view',
- },
- });
- }, 1000);
-
- return () => clearTimeout(timer);
- }, [analytics, getMainInterfaceAnalyticsData]);
-
- return null;
-});
-
-MainInterfaceTracker.displayName = 'MainInterfaceTracker';
-
-export default MainInterfaceTracker;
diff --git a/src/components/Analytics/X.tsx b/src/components/Analytics/X.tsx
new file mode 100644
index 0000000000..21e325287a
--- /dev/null
+++ b/src/components/Analytics/X.tsx
@@ -0,0 +1,48 @@
+'use client';
+
+import { createAnalytics, getSingletonAnalyticsOptional } from '@lobehub/analytics';
+import { memo, useEffect, useRef } from 'react';
+
+import { BUSINESS_LINE } from '@/const/analytics';
+import { isDev } from '@/utils/env';
+
+interface XAdsProps {
+ eventIds?: Record;
+ pixelId?: string;
+ purchaseEventId?: string;
+}
+
+const XAds = memo(({ eventIds, pixelId, purchaseEventId }) => {
+ const analyticsRef = useRef | null>(null);
+
+ useEffect(() => {
+ const singletonAnalytics = getSingletonAnalyticsOptional();
+ if (singletonAnalytics?.getProvider('xAds')) {
+ return;
+ }
+
+ if (!analyticsRef.current) {
+ analyticsRef.current = createAnalytics({
+ business: BUSINESS_LINE,
+ debug: isDev,
+ providers: {
+ xAds: {
+ debug: isDev,
+ eventIds,
+ enabled: !!pixelId,
+ pixelId: pixelId ?? '',
+ purchaseEventId,
+ },
+ },
+ });
+ }
+
+ analyticsRef.current.initialize().catch((error) => {
+ console.error('[X Ads Bootstrap] Initialization failed:', error);
+ });
+ }, [eventIds, pixelId, purchaseEventId]);
+
+ return null;
+});
+
+export default XAds;
diff --git a/src/components/Analytics/index.tsx b/src/components/Analytics/index.tsx
index f24b8999ed..befa2be1ff 100644
--- a/src/components/Analytics/index.tsx
+++ b/src/components/Analytics/index.tsx
@@ -5,6 +5,7 @@ import dynamic from '@/libs/next/dynamic';
import Desktop from './Desktop';
import Google from './Google';
import Vercel from './Vercel';
+import X from './X';
const Plausible = dynamic(() => import('./Plausible'));
const Umami = dynamic(() => import('./Umami'));
@@ -20,6 +21,16 @@ const Analytics = () => {
{analyticsEnv.ENABLE_GOOGLE_ANALYTICS && (
)}
+ {analyticsEnv.ENABLED_X_ADS && (
+
+ )}
{analyticsEnv.ENABLED_PLAUSIBLE_ANALYTICS && (
{
process.env.CLARITY_PROJECT_ID = 'clarity_id';
process.env.ENABLE_VERCEL_ANALYTICS = '1';
process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID = 'ga_id';
+ process.env.X_ADS_PIXEL_ID = 'tw-pixel_id';
+ process.env.X_ADS_LOGIN_OR_SIGNUP_CLICKED_EVENT_ID = 'tw-pixel_id-login_or_signup_clicked';
+ process.env.X_ADS_MAIN_PAGE_VIEW_EVENT_ID = 'tw-pixel_id-main_page_view';
+ process.env.X_ADS_PURCHASE_EVENT_ID = 'tw-pixel_id-purchase_event_id';
const config = getAnalyticsConfig();
@@ -42,6 +46,11 @@ describe('getAnalyticsConfig', () => {
DEBUG_VERCEL_ANALYTICS: false,
ENABLE_GOOGLE_ANALYTICS: true,
GOOGLE_ANALYTICS_MEASUREMENT_ID: 'ga_id',
+ ENABLED_X_ADS: true,
+ X_ADS_PIXEL_ID: 'tw-pixel_id',
+ X_ADS_LOGIN_OR_SIGNUP_CLICKED_EVENT_ID: 'tw-pixel_id-login_or_signup_clicked',
+ X_ADS_MAIN_PAGE_VIEW_EVENT_ID: 'tw-pixel_id-main_page_view',
+ X_ADS_PURCHASE_EVENT_ID: 'tw-pixel_id-purchase_event_id',
});
});
});
diff --git a/src/envs/analytics.ts b/src/envs/analytics.ts
index 4cbec408c6..219f022448 100644
--- a/src/envs/analytics.ts
+++ b/src/envs/analytics.ts
@@ -26,6 +26,12 @@ export const getAnalyticsConfig = () => {
ENABLE_GOOGLE_ANALYTICS: z.boolean(),
GOOGLE_ANALYTICS_MEASUREMENT_ID: z.string().optional(),
+ ENABLED_X_ADS: z.boolean(),
+ X_ADS_PIXEL_ID: z.string().optional(),
+ X_ADS_LOGIN_OR_SIGNUP_CLICKED_EVENT_ID: z.string().optional(),
+ X_ADS_MAIN_PAGE_VIEW_EVENT_ID: z.string().optional(),
+ X_ADS_PURCHASE_EVENT_ID: z.string().optional(),
+
REACT_SCAN_MONITOR_API_KEY: z.string().optional(),
},
runtimeEnv: {
@@ -57,6 +63,13 @@ export const getAnalyticsConfig = () => {
ENABLE_GOOGLE_ANALYTICS: !!process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID,
GOOGLE_ANALYTICS_MEASUREMENT_ID: process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID,
+ // X Ads
+ ENABLED_X_ADS: !!process.env.X_ADS_PIXEL_ID,
+ X_ADS_PIXEL_ID: process.env.X_ADS_PIXEL_ID,
+ X_ADS_LOGIN_OR_SIGNUP_CLICKED_EVENT_ID: process.env.X_ADS_LOGIN_OR_SIGNUP_CLICKED_EVENT_ID,
+ X_ADS_MAIN_PAGE_VIEW_EVENT_ID: process.env.X_ADS_MAIN_PAGE_VIEW_EVENT_ID,
+ X_ADS_PURCHASE_EVENT_ID: process.env.X_ADS_PURCHASE_EVENT_ID,
+
// React Scan Monitor
// https://dashboard.react-scan.com
REACT_SCAN_MONITOR_API_KEY: process.env.REACT_SCAN_MONITOR_API_KEY,
diff --git a/src/features/User/UserLoginOrSignup/Community.tsx b/src/features/User/UserLoginOrSignup/Community.tsx
index 8f2b64b8f8..d19cb42c20 100644
--- a/src/features/User/UserLoginOrSignup/Community.tsx
+++ b/src/features/User/UserLoginOrSignup/Community.tsx
@@ -1,22 +1,15 @@
-import { useAnalytics } from '@lobehub/analytics/react';
import { Button, Flexbox } from '@lobehub/ui';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import UserInfo from '../UserInfo';
+import { trackLoginOrSignupClicked } from './trackLoginOrSignupClicked';
const UserLoginOrSignup = memo<{ onClick: () => void }>(({ onClick }) => {
const { t } = useTranslation('auth');
- const { analytics } = useAnalytics();
const handleClick = () => {
- analytics?.track({
- name: 'login_or_signup_clicked',
- properties: {
- spm: 'homepage.login_or_signup.click',
- },
- });
-
+ void trackLoginOrSignupClicked({ spm: 'homepage.login_or_signup.click' });
onClick();
};
diff --git a/src/features/User/UserLoginOrSignup/trackLoginOrSignupClicked.ts b/src/features/User/UserLoginOrSignup/trackLoginOrSignupClicked.ts
new file mode 100644
index 0000000000..4e2f67de9c
--- /dev/null
+++ b/src/features/User/UserLoginOrSignup/trackLoginOrSignupClicked.ts
@@ -0,0 +1,34 @@
+import { getSingletonAnalyticsOptional } from '@lobehub/analytics';
+
+interface TrackLoginOrSignupClickedParams {
+ provider?: string;
+ spm: string;
+}
+
+export const trackLoginOrSignupClicked = ({
+ provider,
+ spm,
+}: TrackLoginOrSignupClickedParams) => {
+ const analytics = getSingletonAnalyticsOptional();
+ if (!analytics) return Promise.resolve();
+
+ const sendEvent = async () => {
+ const status = analytics.getStatus();
+
+ if (!status.initialized) {
+ await analytics.initialize();
+ }
+
+ await analytics.track({
+ name: 'login_or_signup_clicked',
+ properties: {
+ ...(provider && { provider }),
+ spm,
+ },
+ });
+ };
+
+ return sendEvent().catch((error) => {
+ console.error('Failed to track login_or_signup_clicked:', error);
+ });
+};
diff --git a/src/layout/AnalyticsRSCProvider.tsx b/src/layout/AnalyticsRSCProvider.tsx
new file mode 100644
index 0000000000..9c39e5832b
--- /dev/null
+++ b/src/layout/AnalyticsRSCProvider.tsx
@@ -0,0 +1,45 @@
+import type { ReactNode } from 'react';
+
+import { LobeAnalyticsProvider } from '@/components/Analytics/LobeAnalyticsProvider';
+import { analyticsEnv } from '@/envs/analytics';
+import { isDev } from '@/utils/env';
+
+interface AnalyticsRSCProviderProps {
+ children: ReactNode;
+}
+
+const AnalyticsRSCProvider = ({ children }: AnalyticsRSCProviderProps) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default AnalyticsRSCProvider;
diff --git a/src/routes/(main)/agent/index.tsx b/src/routes/(main)/agent/index.tsx
index f3f0da7a69..b949bdf165 100644
--- a/src/routes/(main)/agent/index.tsx
+++ b/src/routes/(main)/agent/index.tsx
@@ -3,8 +3,6 @@
import { Flexbox } from '@lobehub/ui';
import { memo } from 'react';
-import MainInterfaceTracker from '@/components/Analytics/MainInterfaceTracker';
-
import Conversation from './features/Conversation';
import AgentWorkingSidebar from './features/Conversation/WorkingSidebar';
import PageTitle from './features/PageTitle';
@@ -25,7 +23,6 @@ const ChatPage = memo(() => {
-
>
);
diff --git a/src/routes/(main)/group/index.tsx b/src/routes/(main)/group/index.tsx
index 2514557e77..77acff3106 100644
--- a/src/routes/(main)/group/index.tsx
+++ b/src/routes/(main)/group/index.tsx
@@ -3,8 +3,6 @@
import { Flexbox } from '@lobehub/ui';
import { memo } from 'react';
-import MainInterfaceTracker from '@/components/Analytics/MainInterfaceTracker';
-
import Conversation from './features/Conversation';
import PageTitle from './features/PageTitle';
import Portal from './features/Portal';
@@ -23,7 +21,6 @@ const ChatPage = memo(() => {
-
>
);
diff --git a/src/routes/(main)/home/index.tsx b/src/routes/(main)/home/index.tsx
index 9a7c822c59..40608d8b5d 100644
--- a/src/routes/(main)/home/index.tsx
+++ b/src/routes/(main)/home/index.tsx
@@ -2,6 +2,7 @@ import { Flexbox } from '@lobehub/ui';
import { type FC } from 'react';
import { useLocation } from 'react-router-dom';
+import HomePageTracker from '@/components/Analytics/HomePageTracker';
import PageTitle from '@/components/PageTitle';
import NavHeader from '@/features/NavHeader';
import WideScreenContainer from '@/features/WideScreenContainer';
@@ -15,6 +16,7 @@ const Home: FC = () => {
return (
<>
{isHomeRoute && }
+
} />
diff --git a/src/routes/(mobile)/chat/index.tsx b/src/routes/(mobile)/chat/index.tsx
index a55d43a5a5..93ecfd8b2a 100644
--- a/src/routes/(mobile)/chat/index.tsx
+++ b/src/routes/(mobile)/chat/index.tsx
@@ -2,7 +2,6 @@
import { memo } from 'react';
-import MainInterfaceTracker from '@/components/Analytics/MainInterfaceTracker';
import ConversationArea from '@/routes/(main)/agent/features/Conversation/ConversationArea';
import PageTitle from '@/routes/(main)/agent/features/PageTitle';
import PortalPanel from '@/routes/(main)/agent/features/Portal/features/PortalPanel';
@@ -17,7 +16,6 @@ const MobileChatPage = memo(() => {
-
>
);
diff --git a/src/routes/share/t/[id]/_layout/index.tsx b/src/routes/share/t/[id]/_layout/index.tsx
index d23a48abf5..80bb1e6f49 100644
--- a/src/routes/share/t/[id]/_layout/index.tsx
+++ b/src/routes/share/t/[id]/_layout/index.tsx
@@ -10,6 +10,7 @@ import { Link, Outlet } from 'react-router-dom';
import { ProductLogo } from '@/components/Branding';
import Loading from '@/components/Loading/BrandTextLoading';
+import { trackLoginOrSignupClicked } from '@/features/User/UserLoginOrSignup/trackLoginOrSignupClicked';
import { useIsDark } from '@/hooks/useIsDark';
import { useUserStore } from '@/store/user';
import { authSelectors } from '@/store/user/slices/auth/selectors';
@@ -44,7 +45,18 @@ const ShareTopicLayout = memo(({ children }) => {
) : (
-
+ {
+ event.preventDefault();
+ void trackLoginOrSignupClicked({ spm: 'share.logo_to_signin.click' }).finally(
+ () => {
+ window.location.href = '/signin';
+ },
+ );
+ }}
+ >
)}
diff --git a/src/routes/share/t/[id]/index.tsx b/src/routes/share/t/[id]/index.tsx
index 7cf9f5971b..b353ed0bfb 100644
--- a/src/routes/share/t/[id]/index.tsx
+++ b/src/routes/share/t/[id]/index.tsx
@@ -10,6 +10,7 @@ import useSWR from 'swr';
import NotFound from '@/components/404';
import Loading from '@/components/Loading/BrandTextLoading';
+import { trackLoginOrSignupClicked } from '@/features/User/UserLoginOrSignup/trackLoginOrSignupClicked';
import { lambdaClient } from '@/libs/trpc/client';
import ActionBar from './features/ActionBar';
@@ -59,7 +60,18 @@ const ShareTopicPage = memo(() => {
status={''}
title={t('sharePage.error.unauthorized.title')}
extra={
-