mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 14:56:59 +08:00
chore(core): add mixpanel track (#6202)
This commit is contained in:
@@ -68,6 +68,7 @@
|
||||
"lodash-es": "^4.17.21",
|
||||
"lottie-react": "^2.4.0",
|
||||
"lottie-web": "^5.12.2",
|
||||
"mixpanel-browser": "^2.49.0",
|
||||
"nanoid": "^5.0.6",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "18.2.0",
|
||||
@@ -97,6 +98,7 @@
|
||||
"@types/bytes": "^3.1.4",
|
||||
"@types/image-blob-reduce": "^4.1.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mixpanel-browser": "^2.49.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@vanilla-extract/css": "^1.14.1",
|
||||
"express": "^4.18.2",
|
||||
|
||||
@@ -18,6 +18,7 @@ import { useCallback } from 'react';
|
||||
|
||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||
import { useMutation } from '../../../hooks/use-mutation';
|
||||
import { mixpanel } from '../../../utils';
|
||||
import { emailRegex } from '../../../utils/email-regex';
|
||||
import type { AuthPanelProps } from './index';
|
||||
import { OAuth } from './oauth';
|
||||
@@ -97,6 +98,10 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||
return setAuthState('noAccess');
|
||||
}
|
||||
// TODO, should always get id from user
|
||||
if ('id' in user) {
|
||||
mixpanel.identify(user.id);
|
||||
}
|
||||
setAuthState('afterSignInSendEmail');
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,8 @@ import bytes from 'bytes';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import { mixpanel } from '../../../utils';
|
||||
|
||||
export const CloudQuotaModal = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
const currentWorkspace = useService(Workspace);
|
||||
@@ -70,6 +72,14 @@ export const CloudQuotaModal = () => {
|
||||
};
|
||||
}, [currentWorkspace.engine.blob, setOpen, workspaceQuota.blobLimit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userQuota?.humanReadable) {
|
||||
mixpanel.people.set({
|
||||
plan: userQuota.humanReadable.name,
|
||||
});
|
||||
}
|
||||
}, [userQuota]);
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
open={open}
|
||||
|
||||
@@ -3,8 +3,7 @@ import { WorkspaceManager } from '@toeverything/infra';
|
||||
import { useService } from '@toeverything/infra/di';
|
||||
import { useLiveData } from '@toeverything/infra/livedata';
|
||||
import { useAtom } from 'jotai';
|
||||
import type { ReactElement } from 'react';
|
||||
import { lazy, Suspense, useCallback } from 'react';
|
||||
import { lazy, type ReactElement, Suspense, useCallback } from 'react';
|
||||
|
||||
import {
|
||||
authAtom,
|
||||
@@ -19,6 +18,7 @@ import { useAsyncCallback } from '../hooks/affine-async-hooks';
|
||||
import { useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||
import { CurrentWorkspaceService } from '../modules/workspace/current-workspace';
|
||||
import { WorkspaceSubPath } from '../shared';
|
||||
import { mixpanel } from '../utils';
|
||||
import { signOutCloud } from '../utils/cloud-utils';
|
||||
|
||||
const SettingModal = lazy(() =>
|
||||
@@ -218,6 +218,8 @@ export const SignOutConfirmModal = () => {
|
||||
setOpen(false);
|
||||
await signOutCloud();
|
||||
|
||||
mixpanel.reset();
|
||||
|
||||
// if current workspace is affine cloud, switch to local workspace
|
||||
if (currentWorkspace?.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
|
||||
const localWorkspace = workspaces.find(
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from 'react';
|
||||
|
||||
import { useOnceSignedInEvents } from '../atoms/event';
|
||||
import { mixpanel } from '../utils';
|
||||
|
||||
export const CloudSessionProvider = (props: PropsWithChildren) => {
|
||||
const session = useSession();
|
||||
@@ -28,6 +29,12 @@ export const CloudSessionProvider = (props: PropsWithChildren) => {
|
||||
).postMessage(1);
|
||||
}, [onceSignedInEvents]);
|
||||
|
||||
useEffect(() => {
|
||||
if (session.user?.id) {
|
||||
mixpanel.identify(session.user.id);
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
useEffect(() => {
|
||||
if (prevSession.current !== session && session.status !== 'loading') {
|
||||
// unauthenticated -> authenticated
|
||||
|
||||
@@ -1,59 +1,81 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { wrapCreateBrowserRouter } from '@sentry/react';
|
||||
import { useEffect } from 'react';
|
||||
import type { RouteObject } from 'react-router-dom';
|
||||
import { createBrowserRouter as reactRouterCreateBrowserRouter } from 'react-router-dom';
|
||||
import {
|
||||
createBrowserRouter as reactRouterCreateBrowserRouter,
|
||||
Outlet,
|
||||
useLocation,
|
||||
} from 'react-router-dom';
|
||||
|
||||
export const workbenchRoutes = [
|
||||
import { mixpanel } from './utils';
|
||||
|
||||
function RootRouter() {
|
||||
const location = useLocation();
|
||||
useEffect(() => {
|
||||
mixpanel.track_pageview({
|
||||
page: location.pathname,
|
||||
});
|
||||
}, [location]);
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
export const topLevelRoutes = [
|
||||
{
|
||||
path: '/',
|
||||
lazy: () => import('./pages/index'),
|
||||
},
|
||||
{
|
||||
path: '/workspace/:workspaceId/*',
|
||||
lazy: () => import('./pages/workspace/index'),
|
||||
},
|
||||
{
|
||||
path: '/share/:workspaceId/:pageId',
|
||||
lazy: () => import('./pages/share/share-detail-page'),
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
lazy: () => import('./pages/404'),
|
||||
},
|
||||
{
|
||||
path: '/auth/:authType',
|
||||
lazy: () => import('./pages/auth'),
|
||||
},
|
||||
{
|
||||
path: '/expired',
|
||||
lazy: () => import('./pages/expired'),
|
||||
},
|
||||
{
|
||||
path: '/invite/:inviteId',
|
||||
lazy: () => import('./pages/invite'),
|
||||
},
|
||||
{
|
||||
path: '/signIn',
|
||||
lazy: () => import('./pages/sign-in'),
|
||||
},
|
||||
{
|
||||
path: '/open-app/:action',
|
||||
lazy: () => import('./pages/open-app'),
|
||||
},
|
||||
{
|
||||
path: '/upgrade-success',
|
||||
lazy: () => import('./pages/upgrade-success'),
|
||||
},
|
||||
{
|
||||
path: '/desktop-signin',
|
||||
lazy: () => import('./pages/desktop-signin'),
|
||||
},
|
||||
{
|
||||
path: '/onboarding',
|
||||
lazy: () => import('./pages/onboarding'),
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
lazy: () => import('./pages/404'),
|
||||
element: <RootRouter />,
|
||||
children: [
|
||||
{
|
||||
path: '/',
|
||||
lazy: () => import('./pages/index'),
|
||||
},
|
||||
{
|
||||
path: '/workspace/:workspaceId/*',
|
||||
lazy: () => import('./pages/workspace/index'),
|
||||
},
|
||||
{
|
||||
path: '/share/:workspaceId/:pageId',
|
||||
lazy: () => import('./pages/share/share-detail-page'),
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
lazy: () => import('./pages/404'),
|
||||
},
|
||||
{
|
||||
path: '/auth/:authType',
|
||||
lazy: () => import('./pages/auth'),
|
||||
},
|
||||
{
|
||||
path: '/expired',
|
||||
lazy: () => import('./pages/expired'),
|
||||
},
|
||||
{
|
||||
path: '/invite/:inviteId',
|
||||
lazy: () => import('./pages/invite'),
|
||||
},
|
||||
{
|
||||
path: '/signIn',
|
||||
lazy: () => import('./pages/sign-in'),
|
||||
},
|
||||
{
|
||||
path: '/open-app/:action',
|
||||
lazy: () => import('./pages/open-app'),
|
||||
},
|
||||
{
|
||||
path: '/upgrade-success',
|
||||
lazy: () => import('./pages/upgrade-success'),
|
||||
},
|
||||
{
|
||||
path: '/desktop-signin',
|
||||
lazy: () => import('./pages/desktop-signin'),
|
||||
},
|
||||
{
|
||||
path: '/onboarding',
|
||||
lazy: () => import('./pages/onboarding'),
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
lazy: () => import('./pages/404'),
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies [RouteObject, ...RouteObject[]];
|
||||
|
||||
@@ -92,10 +114,10 @@ export const viewRoutes = [
|
||||
},
|
||||
] satisfies [RouteObject, ...RouteObject[]];
|
||||
|
||||
const createBrowserRouter = Sentry.wrapCreateBrowserRouter(
|
||||
const createBrowserRouter = wrapCreateBrowserRouter(
|
||||
reactRouterCreateBrowserRouter
|
||||
);
|
||||
export const router = createBrowserRouter(workbenchRoutes, {
|
||||
export const router = createBrowserRouter(topLevelRoutes, {
|
||||
future: {
|
||||
v7_normalizeFormMethod: true,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './create-emotion-cache';
|
||||
export * from './intl-formatter';
|
||||
export * from './mixpanel';
|
||||
export * from './string2color';
|
||||
export * from './toast';
|
||||
|
||||
25
packages/frontend/core/src/utils/mixpanel.ts
Normal file
25
packages/frontend/core/src/utils/mixpanel.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import mixpanelBrowser, { type OverridedMixpanel } from 'mixpanel-browser';
|
||||
|
||||
export const mixpanel = process.env.MIXPANEL_TOKEN
|
||||
? mixpanelBrowser
|
||||
: new Proxy(
|
||||
function () {} as unknown as OverridedMixpanel,
|
||||
createProxyHandler()
|
||||
);
|
||||
|
||||
function createProxyHandler(property?: string | symbol) {
|
||||
const handler = {
|
||||
get: (_target, property) => {
|
||||
return new Proxy(
|
||||
function () {} as unknown as OverridedMixpanel,
|
||||
createProxyHandler(property)
|
||||
);
|
||||
},
|
||||
apply: (_target, _thisArg, args) => {
|
||||
console.info(
|
||||
`Mixpanel is not initialized, calling ${property ? String(property) : 'mixpanel'} with args: ${JSON.stringify(args)}`
|
||||
);
|
||||
},
|
||||
} as ProxyHandler<OverridedMixpanel>;
|
||||
return handler;
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@pengx17/electron-forge-maker-appimage": "^1.1.1",
|
||||
"@toeverything/infra": "workspace:*",
|
||||
"@types/mixpanel-browser": "^2.49.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"builder-util-runtime": "^9.2.4",
|
||||
"cross-env": "^7.0.3",
|
||||
@@ -57,6 +58,7 @@
|
||||
"jotai": "^2.6.5",
|
||||
"jotai-devtools": "^0.8.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mixpanel-browser": "^2.49.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
|
||||
@@ -18,6 +18,7 @@ import { createI18n, setUpLanguage } from '@affine/i18n';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||
import { ServiceCollection } from '@toeverything/infra/di';
|
||||
import mixpanel from 'mixpanel-browser';
|
||||
import type { PropsWithChildren, ReactElement } from 'react';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
@@ -62,6 +63,13 @@ const serviceProvider = services.provider();
|
||||
export function App() {
|
||||
performanceRenderLogger.info('App');
|
||||
|
||||
if (process.env.MIXPANEL_TOKEN) {
|
||||
mixpanel.init(process.env.MIXPANEL_TOKEN || '', {
|
||||
track_pageview: true,
|
||||
persistence: 'localStorage',
|
||||
});
|
||||
}
|
||||
|
||||
if (!languageLoadingPromise) {
|
||||
languageLoadingPromise = loadLanguage().catch(console.error);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
"@affine/env": "workspace:*",
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"intl-segmenter-polyfill-rs": "^0.1.7",
|
||||
"mixpanel-browser": "^2.49.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@affine/cli": "workspace:*",
|
||||
"@types/mixpanel-browser": "^2.49.0",
|
||||
"@types/react": "^18.2.60",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"typescript": "^5.3.3"
|
||||
|
||||
@@ -18,6 +18,7 @@ import { createI18n, setUpLanguage } from '@affine/i18n';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||
import { ServiceCollection } from '@toeverything/infra/di';
|
||||
import mixpanel from 'mixpanel-browser';
|
||||
import type { PropsWithChildren, ReactElement } from 'react';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
@@ -62,6 +63,13 @@ const serviceProvider = services.provider();
|
||||
export function App() {
|
||||
performanceRenderLogger.info('App');
|
||||
|
||||
if (process.env.MIXPANEL_TOKEN) {
|
||||
mixpanel.init(process.env.MIXPANEL_TOKEN || '', {
|
||||
track_pageview: true,
|
||||
persistence: 'localStorage',
|
||||
});
|
||||
}
|
||||
|
||||
if (!languageLoadingPromise) {
|
||||
languageLoadingPromise = loadLanguage().catch(console.error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user