chore(core): add mixpanel track (#6202)
This commit is contained in:
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
@@ -15,6 +15,7 @@ on:
|
|||||||
env:
|
env:
|
||||||
APP_NAME: affine
|
APP_NAME: affine
|
||||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||||
|
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-server:
|
build-server:
|
||||||
|
|||||||
1
.github/workflows/release-desktop.yml
vendored
1
.github/workflows/release-desktop.yml
vendored
@@ -33,6 +33,7 @@ env:
|
|||||||
DEBUG: napi:*
|
DEBUG: napi:*
|
||||||
APP_NAME: affine
|
APP_NAME: affine
|
||||||
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
||||||
|
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
before-make:
|
before-make:
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"keyv": "^4.5.4",
|
"keyv": "^4.5.4",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"mixpanel": "^0.18.0",
|
||||||
"nanoid": "^5.0.6",
|
"nanoid": "^5.0.6",
|
||||||
"nest-commander": "^3.12.5",
|
"nest-commander": "^3.12.5",
|
||||||
"nestjs-throttler-storage-redis": "^0.4.1",
|
"nestjs-throttler-storage-redis": "^0.4.1",
|
||||||
@@ -102,6 +103,7 @@
|
|||||||
"@types/graphql-upload": "^16.0.7",
|
"@types/graphql-upload": "^16.0.7",
|
||||||
"@types/keyv": "^4.2.0",
|
"@types/keyv": "^4.2.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/mixpanel": "^2.14.8",
|
||||||
"@types/node": "^20.11.20",
|
"@types/node": "^20.11.20",
|
||||||
"@types/nodemailer": "^6.4.14",
|
"@types/nodemailer": "^6.4.14",
|
||||||
"@types/on-headers": "^1.0.3",
|
"@types/on-headers": "^1.0.3",
|
||||||
|
|||||||
@@ -43,5 +43,12 @@ export async function createApp() {
|
|||||||
app.useWebSocketAdapter(adapter);
|
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;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,6 +333,11 @@ export interface AFFiNEConfig {
|
|||||||
metrics: {
|
metrics: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
telemetry: {
|
||||||
|
enabled: boolean;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from './storage';
|
export * from './storage';
|
||||||
|
|||||||
@@ -192,6 +192,10 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
|
|||||||
metrics: {
|
metrics: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
|
telemetry: {
|
||||||
|
enabled: isSelfhosted && !process.env.DISABLE_SERVER_TELEMETRY,
|
||||||
|
token: '389c0615a69b57cca7d3fa0a4824c930',
|
||||||
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
enabled: new Set(),
|
enabled: new Set(),
|
||||||
use(plugin, config) {
|
use(plugin, config) {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lottie-react": "^2.4.0",
|
"lottie-react": "^2.4.0",
|
||||||
"lottie-web": "^5.12.2",
|
"lottie-web": "^5.12.2",
|
||||||
|
"mixpanel-browser": "^2.49.0",
|
||||||
"nanoid": "^5.0.6",
|
"nanoid": "^5.0.6",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@@ -97,6 +98,7 @@
|
|||||||
"@types/bytes": "^3.1.4",
|
"@types/bytes": "^3.1.4",
|
||||||
"@types/image-blob-reduce": "^4.1.4",
|
"@types/image-blob-reduce": "^4.1.4",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/mixpanel-browser": "^2.49.0",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@vanilla-extract/css": "^1.14.1",
|
"@vanilla-extract/css": "^1.14.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||||
import { useMutation } from '../../../hooks/use-mutation';
|
import { useMutation } from '../../../hooks/use-mutation';
|
||||||
|
import { mixpanel } from '../../../utils';
|
||||||
import { emailRegex } from '../../../utils/email-regex';
|
import { emailRegex } from '../../../utils/email-regex';
|
||||||
import type { AuthPanelProps } from './index';
|
import type { AuthPanelProps } from './index';
|
||||||
import { OAuth } from './oauth';
|
import { OAuth } from './oauth';
|
||||||
@@ -97,6 +98,10 @@ export const SignIn: FC<AuthPanelProps> = ({
|
|||||||
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
if (res?.status === 403 && res?.url === INTERNAL_BETA_URL) {
|
||||||
return setAuthState('noAccess');
|
return setAuthState('noAccess');
|
||||||
}
|
}
|
||||||
|
// TODO, should always get id from user
|
||||||
|
if ('id' in user) {
|
||||||
|
mixpanel.identify(user.id);
|
||||||
|
}
|
||||||
setAuthState('afterSignInSendEmail');
|
setAuthState('afterSignInSendEmail');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import bytes from 'bytes';
|
|||||||
import { useAtom, useSetAtom } from 'jotai';
|
import { useAtom, useSetAtom } from 'jotai';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { mixpanel } from '../../../utils';
|
||||||
|
|
||||||
export const CloudQuotaModal = () => {
|
export const CloudQuotaModal = () => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const currentWorkspace = useService(Workspace);
|
const currentWorkspace = useService(Workspace);
|
||||||
@@ -70,6 +72,14 @@ export const CloudQuotaModal = () => {
|
|||||||
};
|
};
|
||||||
}, [currentWorkspace.engine.blob, setOpen, workspaceQuota.blobLimit]);
|
}, [currentWorkspace.engine.blob, setOpen, workspaceQuota.blobLimit]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userQuota?.humanReadable) {
|
||||||
|
mixpanel.people.set({
|
||||||
|
plan: userQuota.humanReadable.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [userQuota]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
open={open}
|
open={open}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { WorkspaceManager } from '@toeverything/infra';
|
|||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
import { useLiveData } from '@toeverything/infra/livedata';
|
import { useLiveData } from '@toeverything/infra/livedata';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import type { ReactElement } from 'react';
|
import { lazy, type ReactElement, Suspense, useCallback } from 'react';
|
||||||
import { lazy, Suspense, useCallback } from 'react';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
authAtom,
|
authAtom,
|
||||||
@@ -19,6 +18,7 @@ import { useAsyncCallback } from '../hooks/affine-async-hooks';
|
|||||||
import { useNavigateHelper } from '../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||||
import { CurrentWorkspaceService } from '../modules/workspace/current-workspace';
|
import { CurrentWorkspaceService } from '../modules/workspace/current-workspace';
|
||||||
import { WorkspaceSubPath } from '../shared';
|
import { WorkspaceSubPath } from '../shared';
|
||||||
|
import { mixpanel } from '../utils';
|
||||||
import { signOutCloud } from '../utils/cloud-utils';
|
import { signOutCloud } from '../utils/cloud-utils';
|
||||||
|
|
||||||
const SettingModal = lazy(() =>
|
const SettingModal = lazy(() =>
|
||||||
@@ -218,6 +218,8 @@ export const SignOutConfirmModal = () => {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
await signOutCloud();
|
await signOutCloud();
|
||||||
|
|
||||||
|
mixpanel.reset();
|
||||||
|
|
||||||
// if current workspace is affine cloud, switch to local workspace
|
// if current workspace is affine cloud, switch to local workspace
|
||||||
if (currentWorkspace?.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
|
if (currentWorkspace?.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
|
||||||
const localWorkspace = workspaces.find(
|
const localWorkspace = workspaces.find(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { useOnceSignedInEvents } from '../atoms/event';
|
import { useOnceSignedInEvents } from '../atoms/event';
|
||||||
|
import { mixpanel } from '../utils';
|
||||||
|
|
||||||
export const CloudSessionProvider = (props: PropsWithChildren) => {
|
export const CloudSessionProvider = (props: PropsWithChildren) => {
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
@@ -28,6 +29,12 @@ export const CloudSessionProvider = (props: PropsWithChildren) => {
|
|||||||
).postMessage(1);
|
).postMessage(1);
|
||||||
}, [onceSignedInEvents]);
|
}, [onceSignedInEvents]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (session.user?.id) {
|
||||||
|
mixpanel.identify(session.user.id);
|
||||||
|
}
|
||||||
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (prevSession.current !== session && session.status !== 'loading') {
|
if (prevSession.current !== session && session.status !== 'loading') {
|
||||||
// unauthenticated -> authenticated
|
// 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 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: '/',
|
element: <RootRouter />,
|
||||||
lazy: () => import('./pages/index'),
|
children: [
|
||||||
},
|
{
|
||||||
{
|
path: '/',
|
||||||
path: '/workspace/:workspaceId/*',
|
lazy: () => import('./pages/index'),
|
||||||
lazy: () => import('./pages/workspace/index'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/workspace/:workspaceId/*',
|
||||||
path: '/share/:workspaceId/:pageId',
|
lazy: () => import('./pages/workspace/index'),
|
||||||
lazy: () => import('./pages/share/share-detail-page'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/share/:workspaceId/:pageId',
|
||||||
path: '/404',
|
lazy: () => import('./pages/share/share-detail-page'),
|
||||||
lazy: () => import('./pages/404'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/404',
|
||||||
path: '/auth/:authType',
|
lazy: () => import('./pages/404'),
|
||||||
lazy: () => import('./pages/auth'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/auth/:authType',
|
||||||
path: '/expired',
|
lazy: () => import('./pages/auth'),
|
||||||
lazy: () => import('./pages/expired'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/expired',
|
||||||
path: '/invite/:inviteId',
|
lazy: () => import('./pages/expired'),
|
||||||
lazy: () => import('./pages/invite'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/invite/:inviteId',
|
||||||
path: '/signIn',
|
lazy: () => import('./pages/invite'),
|
||||||
lazy: () => import('./pages/sign-in'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/signIn',
|
||||||
path: '/open-app/:action',
|
lazy: () => import('./pages/sign-in'),
|
||||||
lazy: () => import('./pages/open-app'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/open-app/:action',
|
||||||
path: '/upgrade-success',
|
lazy: () => import('./pages/open-app'),
|
||||||
lazy: () => import('./pages/upgrade-success'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/upgrade-success',
|
||||||
path: '/desktop-signin',
|
lazy: () => import('./pages/upgrade-success'),
|
||||||
lazy: () => import('./pages/desktop-signin'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/desktop-signin',
|
||||||
path: '/onboarding',
|
lazy: () => import('./pages/desktop-signin'),
|
||||||
lazy: () => import('./pages/onboarding'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/onboarding',
|
||||||
path: '*',
|
lazy: () => import('./pages/onboarding'),
|
||||||
lazy: () => import('./pages/404'),
|
},
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
lazy: () => import('./pages/404'),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
] satisfies [RouteObject, ...RouteObject[]];
|
] satisfies [RouteObject, ...RouteObject[]];
|
||||||
|
|
||||||
@@ -92,10 +114,10 @@ export const viewRoutes = [
|
|||||||
},
|
},
|
||||||
] satisfies [RouteObject, ...RouteObject[]];
|
] satisfies [RouteObject, ...RouteObject[]];
|
||||||
|
|
||||||
const createBrowserRouter = Sentry.wrapCreateBrowserRouter(
|
const createBrowserRouter = wrapCreateBrowserRouter(
|
||||||
reactRouterCreateBrowserRouter
|
reactRouterCreateBrowserRouter
|
||||||
);
|
);
|
||||||
export const router = createBrowserRouter(workbenchRoutes, {
|
export const router = createBrowserRouter(topLevelRoutes, {
|
||||||
future: {
|
future: {
|
||||||
v7_normalizeFormMethod: true,
|
v7_normalizeFormMethod: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './create-emotion-cache';
|
export * from './create-emotion-cache';
|
||||||
export * from './intl-formatter';
|
export * from './intl-formatter';
|
||||||
|
export * from './mixpanel';
|
||||||
export * from './string2color';
|
export * from './string2color';
|
||||||
export * from './toast';
|
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",
|
"@emotion/react": "^11.11.4",
|
||||||
"@pengx17/electron-forge-maker-appimage": "^1.1.1",
|
"@pengx17/electron-forge-maker-appimage": "^1.1.1",
|
||||||
"@toeverything/infra": "workspace:*",
|
"@toeverything/infra": "workspace:*",
|
||||||
|
"@types/mixpanel-browser": "^2.49.0",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"builder-util-runtime": "^9.2.4",
|
"builder-util-runtime": "^9.2.4",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
"jotai": "^2.6.5",
|
"jotai": "^2.6.5",
|
||||||
"jotai-devtools": "^0.8.0",
|
"jotai-devtools": "^0.8.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"mixpanel-browser": "^2.49.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { createI18n, setUpLanguage } from '@affine/i18n';
|
|||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||||
import { ServiceCollection } from '@toeverything/infra/di';
|
import { ServiceCollection } from '@toeverything/infra/di';
|
||||||
|
import mixpanel from 'mixpanel-browser';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { lazy, Suspense } from 'react';
|
import { lazy, Suspense } from 'react';
|
||||||
import { RouterProvider } from 'react-router-dom';
|
import { RouterProvider } from 'react-router-dom';
|
||||||
@@ -62,6 +63,13 @@ const serviceProvider = services.provider();
|
|||||||
export function App() {
|
export function App() {
|
||||||
performanceRenderLogger.info('App');
|
performanceRenderLogger.info('App');
|
||||||
|
|
||||||
|
if (process.env.MIXPANEL_TOKEN) {
|
||||||
|
mixpanel.init(process.env.MIXPANEL_TOKEN || '', {
|
||||||
|
track_pageview: true,
|
||||||
|
persistence: 'localStorage',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!languageLoadingPromise) {
|
if (!languageLoadingPromise) {
|
||||||
languageLoadingPromise = loadLanguage().catch(console.error);
|
languageLoadingPromise = loadLanguage().catch(console.error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,13 @@
|
|||||||
"@affine/env": "workspace:*",
|
"@affine/env": "workspace:*",
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"intl-segmenter-polyfill-rs": "^0.1.7",
|
"intl-segmenter-polyfill-rs": "^0.1.7",
|
||||||
|
"mixpanel-browser": "^2.49.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@affine/cli": "workspace:*",
|
"@affine/cli": "workspace:*",
|
||||||
|
"@types/mixpanel-browser": "^2.49.0",
|
||||||
"@types/react": "^18.2.60",
|
"@types/react": "^18.2.60",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { createI18n, setUpLanguage } from '@affine/i18n';
|
|||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||||
import { ServiceCollection } from '@toeverything/infra/di';
|
import { ServiceCollection } from '@toeverything/infra/di';
|
||||||
|
import mixpanel from 'mixpanel-browser';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { lazy, Suspense } from 'react';
|
import { lazy, Suspense } from 'react';
|
||||||
import { RouterProvider } from 'react-router-dom';
|
import { RouterProvider } from 'react-router-dom';
|
||||||
@@ -62,6 +63,13 @@ const serviceProvider = services.provider();
|
|||||||
export function App() {
|
export function App() {
|
||||||
performanceRenderLogger.info('App');
|
performanceRenderLogger.info('App');
|
||||||
|
|
||||||
|
if (process.env.MIXPANEL_TOKEN) {
|
||||||
|
mixpanel.init(process.env.MIXPANEL_TOKEN || '', {
|
||||||
|
track_pageview: true,
|
||||||
|
persistence: 'localStorage',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!languageLoadingPromise) {
|
if (!languageLoadingPromise) {
|
||||||
languageLoadingPromise = loadLanguage().catch(console.error);
|
languageLoadingPromise = loadLanguage().catch(console.error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NavigateContext } from '@affine/core/hooks/use-navigate-helper';
|
import { NavigateContext } from '@affine/core/hooks/use-navigate-helper';
|
||||||
import { workbenchRoutes } from '@affine/core/router';
|
import { topLevelRoutes } from '@affine/core/router';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import type { StoryFn } from '@storybook/react';
|
import type { StoryFn } from '@storybook/react';
|
||||||
import { screen, userEvent, waitFor, within } from '@storybook/testing-library';
|
import { screen, userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
@@ -34,7 +34,7 @@ export const Index: StoryFn = () => {
|
|||||||
Index.decorators = [withRouter];
|
Index.decorators = [withRouter];
|
||||||
Index.parameters = {
|
Index.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ SettingPage.play = async ({ canvasElement, step }) => {
|
|||||||
SettingPage.decorators = [withRouter];
|
SettingPage.decorators = [withRouter];
|
||||||
SettingPage.parameters = {
|
SettingPage.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ export const NotFoundPage: StoryFn = () => {
|
|||||||
NotFoundPage.decorators = [withRouter];
|
NotFoundPage.decorators = [withRouter];
|
||||||
NotFoundPage.parameters = {
|
NotFoundPage.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
location: {
|
location: {
|
||||||
path: '/404',
|
path: '/404',
|
||||||
},
|
},
|
||||||
@@ -120,7 +120,7 @@ WorkspaceList.play = async ({ canvasElement }) => {
|
|||||||
WorkspaceList.decorators = [withRouter];
|
WorkspaceList.decorators = [withRouter];
|
||||||
WorkspaceList.parameters = {
|
WorkspaceList.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
location: {
|
location: {
|
||||||
path: '/',
|
path: '/',
|
||||||
},
|
},
|
||||||
@@ -157,7 +157,7 @@ SearchPage.play = async ({ canvasElement }) => {
|
|||||||
SearchPage.decorators = [withRouter];
|
SearchPage.decorators = [withRouter];
|
||||||
SearchPage.parameters = {
|
SearchPage.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
location: {
|
location: {
|
||||||
path: '/',
|
path: '/',
|
||||||
},
|
},
|
||||||
@@ -199,7 +199,7 @@ ImportPage.play = async ({ canvasElement }) => {
|
|||||||
ImportPage.decorators = [withRouter];
|
ImportPage.decorators = [withRouter];
|
||||||
ImportPage.parameters = {
|
ImportPage.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
location: {
|
location: {
|
||||||
path: '/',
|
path: '/',
|
||||||
},
|
},
|
||||||
@@ -212,7 +212,7 @@ export const OpenAppPage: StoryFn = () => {
|
|||||||
OpenAppPage.decorators = [withRouter];
|
OpenAppPage.decorators = [withRouter];
|
||||||
OpenAppPage.parameters = {
|
OpenAppPage.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
location: {
|
location: {
|
||||||
path: '/open-app/url',
|
path: '/open-app/url',
|
||||||
searchParams: {
|
searchParams: {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
PageTags,
|
PageTags,
|
||||||
type PageTagsProps,
|
type PageTagsProps,
|
||||||
} from '@affine/core/components/page-list';
|
} from '@affine/core/components/page-list';
|
||||||
import { workbenchRoutes } from '@affine/core/router';
|
import { topLevelRoutes } from '@affine/core/router';
|
||||||
import { AffineSchemas } from '@blocksuite/blocks/schemas';
|
import { AffineSchemas } from '@blocksuite/blocks/schemas';
|
||||||
import { PageIcon, TagsIcon } from '@blocksuite/icons';
|
import { PageIcon, TagsIcon } from '@blocksuite/icons';
|
||||||
import { DocCollection, Schema } from '@blocksuite/store';
|
import { DocCollection, Schema } from '@blocksuite/store';
|
||||||
@@ -49,7 +49,7 @@ AffineOperationCell.args = {
|
|||||||
};
|
};
|
||||||
AffineOperationCell.parameters = {
|
AffineOperationCell.parameters = {
|
||||||
reactRouter: reactRouterParameters({
|
reactRouter: reactRouterParameters({
|
||||||
routing: reactRouterOutlets(workbenchRoutes),
|
routing: reactRouterOutlets(topLevelRoutes),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
AffineOperationCell.play = async ({ canvasElement }) => {
|
AffineOperationCell.play = async ({ canvasElement }) => {
|
||||||
|
|||||||
48
yarn.lock
48
yarn.lock
@@ -362,6 +362,7 @@ __metadata:
|
|||||||
"@types/bytes": "npm:^3.1.4"
|
"@types/bytes": "npm:^3.1.4"
|
||||||
"@types/image-blob-reduce": "npm:^4.1.4"
|
"@types/image-blob-reduce": "npm:^4.1.4"
|
||||||
"@types/lodash-es": "npm:^4.17.12"
|
"@types/lodash-es": "npm:^4.17.12"
|
||||||
|
"@types/mixpanel-browser": "npm:^2.49.0"
|
||||||
"@types/uuid": "npm:^9.0.8"
|
"@types/uuid": "npm:^9.0.8"
|
||||||
"@vanilla-extract/css": "npm:^1.14.1"
|
"@vanilla-extract/css": "npm:^1.14.1"
|
||||||
"@vanilla-extract/dynamic": "npm:^2.1.0"
|
"@vanilla-extract/dynamic": "npm:^2.1.0"
|
||||||
@@ -389,6 +390,7 @@ __metadata:
|
|||||||
lottie-react: "npm:^2.4.0"
|
lottie-react: "npm:^2.4.0"
|
||||||
lottie-web: "npm:^5.12.2"
|
lottie-web: "npm:^5.12.2"
|
||||||
mime-types: "npm:^2.1.35"
|
mime-types: "npm:^2.1.35"
|
||||||
|
mixpanel-browser: "npm:^2.49.0"
|
||||||
nanoid: "npm:^5.0.6"
|
nanoid: "npm:^5.0.6"
|
||||||
next-themes: "npm:^0.3.0"
|
next-themes: "npm:^0.3.0"
|
||||||
react: "npm:18.2.0"
|
react: "npm:18.2.0"
|
||||||
@@ -465,6 +467,7 @@ __metadata:
|
|||||||
"@emotion/react": "npm:^11.11.4"
|
"@emotion/react": "npm:^11.11.4"
|
||||||
"@pengx17/electron-forge-maker-appimage": "npm:^1.1.1"
|
"@pengx17/electron-forge-maker-appimage": "npm:^1.1.1"
|
||||||
"@toeverything/infra": "workspace:*"
|
"@toeverything/infra": "workspace:*"
|
||||||
|
"@types/mixpanel-browser": "npm:^2.49.0"
|
||||||
"@types/uuid": "npm:^9.0.8"
|
"@types/uuid": "npm:^9.0.8"
|
||||||
async-call-rpc: "npm:^6.4.0"
|
async-call-rpc: "npm:^6.4.0"
|
||||||
builder-util-runtime: "npm:^9.2.4"
|
builder-util-runtime: "npm:^9.2.4"
|
||||||
@@ -481,6 +484,7 @@ __metadata:
|
|||||||
jotai-devtools: "npm:^0.8.0"
|
jotai-devtools: "npm:^0.8.0"
|
||||||
link-preview-js: "npm:^3.0.5"
|
link-preview-js: "npm:^3.0.5"
|
||||||
lodash-es: "npm:^4.17.21"
|
lodash-es: "npm:^4.17.21"
|
||||||
|
mixpanel-browser: "npm:^2.49.0"
|
||||||
nanoid: "npm:^5.0.6"
|
nanoid: "npm:^5.0.6"
|
||||||
react: "npm:^18.2.0"
|
react: "npm:^18.2.0"
|
||||||
react-dom: "npm:^18.2.0"
|
react-dom: "npm:^18.2.0"
|
||||||
@@ -684,6 +688,7 @@ __metadata:
|
|||||||
"@types/graphql-upload": "npm:^16.0.7"
|
"@types/graphql-upload": "npm:^16.0.7"
|
||||||
"@types/keyv": "npm:^4.2.0"
|
"@types/keyv": "npm:^4.2.0"
|
||||||
"@types/lodash-es": "npm:^4.17.12"
|
"@types/lodash-es": "npm:^4.17.12"
|
||||||
|
"@types/mixpanel": "npm:^2.14.8"
|
||||||
"@types/node": "npm:^20.11.20"
|
"@types/node": "npm:^20.11.20"
|
||||||
"@types/nodemailer": "npm:^6.4.14"
|
"@types/nodemailer": "npm:^6.4.14"
|
||||||
"@types/on-headers": "npm:^1.0.3"
|
"@types/on-headers": "npm:^1.0.3"
|
||||||
@@ -706,6 +711,7 @@ __metadata:
|
|||||||
ioredis: "npm:^5.3.2"
|
ioredis: "npm:^5.3.2"
|
||||||
keyv: "npm:^4.5.4"
|
keyv: "npm:^4.5.4"
|
||||||
lodash-es: "npm:^4.17.21"
|
lodash-es: "npm:^4.17.21"
|
||||||
|
mixpanel: "npm:^0.18.0"
|
||||||
nanoid: "npm:^5.0.6"
|
nanoid: "npm:^5.0.6"
|
||||||
nest-commander: "npm:^3.12.5"
|
nest-commander: "npm:^3.12.5"
|
||||||
nestjs-throttler-storage-redis: "npm:^0.4.1"
|
nestjs-throttler-storage-redis: "npm:^0.4.1"
|
||||||
@@ -818,9 +824,11 @@ __metadata:
|
|||||||
"@affine/core": "workspace:*"
|
"@affine/core": "workspace:*"
|
||||||
"@affine/env": "workspace:*"
|
"@affine/env": "workspace:*"
|
||||||
"@juggle/resize-observer": "npm:^3.4.0"
|
"@juggle/resize-observer": "npm:^3.4.0"
|
||||||
|
"@types/mixpanel-browser": "npm:^2.49.0"
|
||||||
"@types/react": "npm:^18.2.60"
|
"@types/react": "npm:^18.2.60"
|
||||||
"@types/react-dom": "npm:^18.2.19"
|
"@types/react-dom": "npm:^18.2.19"
|
||||||
intl-segmenter-polyfill-rs: "npm:^0.1.7"
|
intl-segmenter-polyfill-rs: "npm:^0.1.7"
|
||||||
|
mixpanel-browser: "npm:^2.49.0"
|
||||||
react: "npm:^18.2.0"
|
react: "npm:^18.2.0"
|
||||||
react-dom: "npm:^18.2.0"
|
react-dom: "npm:^18.2.0"
|
||||||
typescript: "npm:^5.3.3"
|
typescript: "npm:^5.3.3"
|
||||||
@@ -14280,6 +14288,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/mixpanel-browser@npm:^2.49.0":
|
||||||
|
version: 2.49.0
|
||||||
|
resolution: "@types/mixpanel-browser@npm:2.49.0"
|
||||||
|
checksum: 10/8aa9f114098cd9e2b06314586d73934aa83a8116b7de61bd41208728aebd47daa30134a98c4af3d65c4e755fbbdb6c450058a34ad2204305587fbe8e27f844b3
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/mixpanel@npm:^2.14.8":
|
||||||
|
version: 2.14.8
|
||||||
|
resolution: "@types/mixpanel@npm:2.14.8"
|
||||||
|
checksum: 10/b0be92f54226986aa7e4a789692d223008673f3eb42cb2a52fe65089c15dc1ee21bbe083c14a8867fe9a2ed32925510cbd772a6ea4aebb5b93460ece9bd602a7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/ms@npm:*":
|
"@types/ms@npm:*":
|
||||||
version: 0.7.34
|
version: 0.7.34
|
||||||
resolution: "@types/ms@npm:0.7.34"
|
resolution: "@types/ms@npm:0.7.34"
|
||||||
@@ -23243,6 +23265,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"https-proxy-agent@npm:5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "https-proxy-agent@npm:5.0.0"
|
||||||
|
dependencies:
|
||||||
|
agent-base: "npm:6"
|
||||||
|
debug: "npm:4"
|
||||||
|
checksum: 10/517037badcbbe30757a9a88aaf5e8c198d31aa0b1e9c0a49a0053ab8e812809242218cc9ea1929171f74d95ae1ec89782ba471ffc3709b8910e91d1761f5f1a6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"https-proxy-agent@npm:^4.0.0":
|
"https-proxy-agent@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "https-proxy-agent@npm:4.0.0"
|
resolution: "https-proxy-agent@npm:4.0.0"
|
||||||
@@ -27642,6 +27674,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"mixpanel-browser@npm:^2.49.0":
|
||||||
|
version: 2.49.0
|
||||||
|
resolution: "mixpanel-browser@npm:2.49.0"
|
||||||
|
checksum: 10/46b738792153caa35c083f3e23b208d48fac0f040074d2f36dc95bac49e86729676ffb599b4b50d814231c4ae8170110419e748a1cbe0d5432ea415f0fe8adc4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"mixpanel@npm:^0.18.0":
|
||||||
|
version: 0.18.0
|
||||||
|
resolution: "mixpanel@npm:0.18.0"
|
||||||
|
dependencies:
|
||||||
|
https-proxy-agent: "npm:5.0.0"
|
||||||
|
checksum: 10/90cd6be667e75d5a34fc54c2b7947f9d095736f8925d68586143f73ad525503d3951e852b3ae4349df04f21a1fa810325023d20126c145ebaff96b03745b732c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"mkdirp-classic@npm:^0.5.2":
|
"mkdirp-classic@npm:^0.5.2":
|
||||||
version: 0.5.3
|
version: 0.5.3
|
||||||
resolution: "mkdirp-classic@npm:0.5.3"
|
resolution: "mkdirp-classic@npm:0.5.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user