Skip to content

Commit

Permalink
chore(core): add mixpanel track
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Mar 22, 2024
1 parent 7535586 commit 263b02d
Show file tree
Hide file tree
Showing 21 changed files with 230 additions and 68 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ on:
env:
APP_NAME: affine
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'

jobs:
build-server:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ env:
DEBUG: napi:*
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '10.13'
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'

jobs:
before-make:
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"ioredis": "^5.3.2",
"keyv": "^4.5.4",
"lodash-es": "^4.17.21",
"mixpanel": "^0.18.0",
"nanoid": "^5.0.6",
"nest-commander": "^3.12.5",
"nestjs-throttler-storage-redis": "^0.4.1",
Expand Down Expand Up @@ -102,6 +103,7 @@
"@types/graphql-upload": "^16.0.7",
"@types/keyv": "^4.2.0",
"@types/lodash-es": "^4.17.12",
"@types/mixpanel": "^2.14.8",
"@types/node": "^20.11.20",
"@types/nodemailer": "^6.4.14",
"@types/on-headers": "^1.0.3",
Expand Down
7 changes: 7 additions & 0 deletions packages/backend/server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,12 @@ export async function createApp() {
app.useWebSocketAdapter(adapter);
}

if (AFFiNE.isSelfhosted && AFFiNE.telemetry.enabled) {
const mixpanel = await import('mixpanel');
mixpanel.init(AFFiNE.telemetry.token).track('selfhost-server-started', {
version: AFFiNE.version,
});
}

return app;
}
5 changes: 5 additions & 0 deletions packages/backend/server/src/fundamentals/config/def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ export interface AFFiNEConfig {
metrics: {
enabled: boolean;
};

telemetry: {
enabled: boolean;
token: string;
};
}

export * from './storage';
4 changes: 4 additions & 0 deletions packages/backend/server/src/fundamentals/config/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
metrics: {
enabled: false,
},
telemetry: {
enabled: isSelfhosted && !process.env.DISABLE_SERVER_TELEMETRY,
token: '389c0615a69b57cca7d3fa0a4824c930',
},
plugins: {
enabled: new Set(),
use(plugin, config) {
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions packages/frontend/core/src/components/affine/auth/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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}
Expand Down
6 changes: 4 additions & 2 deletions packages/frontend/core/src/providers/modal-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(() =>
Expand Down Expand Up @@ -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(
Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/core/src/providers/session-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from 'react';

import { useOnceSignedInEvents } from '../atoms/event';
import { mixpanel } from '../utils';

export const CloudSessionProvider = (props: PropsWithChildren) => {
const session = useSession();
Expand All @@ -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
Expand Down
134 changes: 78 additions & 56 deletions packages/frontend/core/src/router.tsx
Original file line number Diff line number Diff line change
@@ -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 = [
{
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'),
import { mixpanel } from './utils';

function RootRouter() {
const location = useLocation();
useEffect(() => {
mixpanel.track_pageview({
page: location.pathname,
});
}, [location]);
return <Outlet />;
}

export const topLevelRoutes = [
{
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[]];

Expand Down Expand Up @@ -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,
},
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/core/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './create-emotion-cache';
export * from './intl-formatter';
export * from './mixpanel';
export * from './string2color';
export * from './toast';
25 changes: 25 additions & 0 deletions packages/frontend/core/src/utils/mixpanel.ts
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 2 additions & 0 deletions packages/frontend/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
8 changes: 8 additions & 0 deletions packages/frontend/electron/renderer/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit 263b02d

Please sign in to comment.