mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-05 09:04:56 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ebe8f5fb4 | ||
|
|
f94306703a | ||
|
|
3e23878e0f | ||
|
|
bd1733b2a9 | ||
|
|
31f7f6c9cf | ||
|
|
8af064b663 | ||
|
|
b8a1fbd6c7 | ||
|
|
e2b057cb93 | ||
|
|
9ac8f3177e | ||
|
|
931e9968b8 | ||
|
|
c07c7c0969 | ||
|
|
f5dceda0cc | ||
|
|
203459679c |
2
.github/workflows/release-desktop.yml
vendored
2
.github/workflows/release-desktop.yml
vendored
@@ -123,7 +123,7 @@ jobs:
|
||||
|
||||
- name: Signing By Apple Developer ID
|
||||
if: ${{ matrix.spec.platform == 'darwin' }}
|
||||
uses: apple-actions/import-codesign-certs@v2
|
||||
uses: apple-actions/import-codesign-certs@v3
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@istanbuljs/schema": "^0.1.3",
|
||||
"@magic-works/i18n-codegen": "^0.6.0",
|
||||
"@nx/vite": "19.0.1",
|
||||
"@nx/vite": "19.0.2",
|
||||
"@playwright/test": "^1.44.0",
|
||||
"@taplo/cli": "^0.7.0",
|
||||
"@testing-library/react": "^15.0.0",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"build:debug": "napi build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "3.0.0-alpha.54",
|
||||
"@napi-rs/cli": "3.0.0-alpha.55",
|
||||
"lib0": "^0.2.93",
|
||||
"nx": "^19.0.0",
|
||||
"nx-cloud": "^18.0.0",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"dependencies": {
|
||||
"@apollo/server": "^4.10.2",
|
||||
"@aws-sdk/client-s3": "^3.552.0",
|
||||
"@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.17.0",
|
||||
"@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.18.0",
|
||||
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.1.0",
|
||||
"@google-cloud/opentelemetry-resource-util": "^2.1.0",
|
||||
"@keyv/redis": "^2.8.4",
|
||||
|
||||
4
packages/common/env/package.json
vendored
4
packages/common/env/package.json
vendored
@@ -3,8 +3,8 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@blocksuite/global": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/store": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/global": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/store": "0.14.0-canary-202405100201-e591bb8",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"vitest": "1.6.0"
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
"@affine/debug": "workspace:*",
|
||||
"@affine/env": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/global": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/store": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/global": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/store": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@datastructures-js/binary-search-tree": "^5.3.2",
|
||||
"foxact": "^0.2.33",
|
||||
"jotai": "^2.8.0",
|
||||
@@ -28,8 +28,8 @@
|
||||
"devDependencies": {
|
||||
"@affine-test/fixtures": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@testing-library/react": "^15.0.0",
|
||||
"async-call-rpc": "^6.4.0",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -37,6 +37,10 @@ export class EventBus {
|
||||
}
|
||||
}
|
||||
|
||||
get root(): EventBus {
|
||||
return this.parent?.root ?? this;
|
||||
}
|
||||
|
||||
on<T>(id: string, listener: (event: FrameworkEvent<T>) => void) {
|
||||
if (!this.listeners[id]) {
|
||||
this.listeners[id] = [];
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/global": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/global": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/icons": "2.1.50",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/store": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/store": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@storybook/addon-actions": "^7.6.17",
|
||||
"@storybook/addon-essentials": "^7.6.17",
|
||||
"@storybook/addon-interactions": "^7.6.17",
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
"@affine/graphql": "workspace:*",
|
||||
"@affine/i18n": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/global": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/global": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/icons": "2.1.50",
|
||||
"@blocksuite/inline": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/store": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/inline": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/store": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/modifiers": "^7.0.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { createStore } from 'jotai';
|
||||
|
||||
import { openSettingModalAtom, openWorkspaceListModalAtom } from '../atoms';
|
||||
import type { useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||
import { mixpanel } from '../utils/mixpanel';
|
||||
|
||||
export function registerAffineNavigationCommands({
|
||||
t,
|
||||
@@ -76,6 +77,10 @@ export function registerAffineNavigationCommands({
|
||||
label: t['com.affine.cmdk.affine.navigation.open-settings'](),
|
||||
keyBinding: '$mod+,',
|
||||
run() {
|
||||
mixpanel.track('SettingsViewed', {
|
||||
// page:
|
||||
segment: 'cmdk',
|
||||
});
|
||||
store.set(openSettingModalAtom, s => ({
|
||||
activeTab: 'appearance',
|
||||
open: !s.open,
|
||||
@@ -84,6 +89,25 @@ export function registerAffineNavigationCommands({
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: 'affine:open-account',
|
||||
category: 'affine:navigation',
|
||||
icon: <ArrowRightBigIcon />,
|
||||
label: t['com.affine.cmdk.affine.navigation.open-account-settings'](),
|
||||
run() {
|
||||
mixpanel.track('AccountSettingsViewed', {
|
||||
// page:
|
||||
segment: 'cmdk',
|
||||
});
|
||||
store.set(openSettingModalAtom, s => ({
|
||||
activeTab: 'account',
|
||||
open: !s.open,
|
||||
}));
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
registerAffineCommand({
|
||||
id: 'affine:goto-trash',
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { useTheme } from 'next-themes';
|
||||
|
||||
import { openQuickSearchModalAtom } from '../atoms';
|
||||
import type { useLanguageHelper } from '../hooks/affine/use-language-helper';
|
||||
import { mixpanel } from '../utils';
|
||||
|
||||
export function registerAffineSettingsCommands({
|
||||
t,
|
||||
@@ -38,6 +39,9 @@ export function registerAffineSettingsCommands({
|
||||
label: '',
|
||||
icon: <SettingsIcon />,
|
||||
run() {
|
||||
mixpanel.track('QuickSearchOpened', {
|
||||
control: 'shortcut',
|
||||
});
|
||||
const quickSearchModalState = store.get(openQuickSearchModalAtom);
|
||||
|
||||
if (!editor) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Button, FlexWrapper, notify } from '@affine/component';
|
||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { AiIcon } from '@blocksuite/icons';
|
||||
@@ -69,6 +70,11 @@ export const AIOnboardingEdgeless = ({
|
||||
const mode = useLiveData(doc.mode$);
|
||||
|
||||
const goToPricingPlans = useCallback(() => {
|
||||
mixpanel.track('PlansViewed', {
|
||||
page: 'whiteboard editor',
|
||||
segment: 'ai onboarding',
|
||||
module: 'whiteboard dialog',
|
||||
});
|
||||
setSettingModal({
|
||||
open: true,
|
||||
activeTab: 'plans',
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Button, IconButton, Modal } from '@affine/component';
|
||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||
import { useBlurRoot } from '@affine/core/hooks/use-blur-root';
|
||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
@@ -122,6 +123,11 @@ export const AIOnboardingGeneral = ({
|
||||
activeTab: 'plans',
|
||||
scrollAnchor: 'aiPricingPlan',
|
||||
});
|
||||
mixpanel.track('PlansViewed', {
|
||||
page: 'whiteboard-editor',
|
||||
segment: 'ai onboarding',
|
||||
module: 'general',
|
||||
});
|
||||
closeAndDismiss();
|
||||
}, [closeAndDismiss, setSettingModal]);
|
||||
const onPrev = useCallback(() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useLiveData, useServices } from '@toeverything/infra';
|
||||
import { useSetAtom } from 'jotai';
|
||||
@@ -40,6 +41,10 @@ export const UserPlanButton = () => {
|
||||
open: true,
|
||||
activeTab: 'plans',
|
||||
});
|
||||
mixpanel.track('PlansViewed', {
|
||||
segment: 'settings panel',
|
||||
module: 'profile and badge',
|
||||
});
|
||||
},
|
||||
[setSettingModalAtom]
|
||||
);
|
||||
|
||||
@@ -225,6 +225,9 @@ const PlanPrompt = () => {
|
||||
open: true,
|
||||
activeTab: 'plans',
|
||||
});
|
||||
mixpanel.track('PlansViewed', {
|
||||
segment: 'doc history',
|
||||
});
|
||||
}, [setSettingModalAtom]);
|
||||
|
||||
const t = useAFFiNEI18N();
|
||||
@@ -233,7 +236,7 @@ const PlanPrompt = () => {
|
||||
return (
|
||||
<div className={styles.planPromptTitle}>
|
||||
{
|
||||
isProWorkspace === null
|
||||
isProWorkspace !== null
|
||||
? !isProWorkspace
|
||||
? t[
|
||||
'com.affine.history.confirm-restore-modal.plan-prompt.limited-title'
|
||||
|
||||
@@ -3,6 +3,7 @@ import { openQuotaModalAtom, openSettingModalAtom } from '@affine/core/atoms';
|
||||
import { UserQuotaService } from '@affine/core/modules/cloud';
|
||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||
import bytes from 'bytes';
|
||||
@@ -48,6 +49,11 @@ export const CloudQuotaModal = () => {
|
||||
activeTab: 'plans',
|
||||
});
|
||||
|
||||
mixpanel.track('PlansViewed', {
|
||||
segment: 'payment wall',
|
||||
category: 'payment wall storage',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
}, [setOpen, setSettingModalAtom]);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
SubscriptionService,
|
||||
UserCopilotQuotaService,
|
||||
} from '@affine/core/modules/cloud';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
@@ -46,6 +47,12 @@ export const AIUsagePanel = () => {
|
||||
open: true,
|
||||
activeTab: 'billing',
|
||||
});
|
||||
mixpanel.track('BillingViewed', {
|
||||
segment: 'settings panel',
|
||||
module: 'account usage list',
|
||||
control: 'change plan button',
|
||||
type: 'ai subscription',
|
||||
});
|
||||
}, [setOpenSettingModal]);
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -162,8 +162,11 @@ const StoragePanel = () => {
|
||||
|
||||
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
const onUpgrade = useCallback(() => {
|
||||
mixpanel.track('Button', {
|
||||
resolve: 'UpgradeStorage',
|
||||
mixpanel.track('PlansViewed', {
|
||||
segment: 'settings panel',
|
||||
module: 'account usage list',
|
||||
control: 'cloud storage upgrade button',
|
||||
type: 'cloud subscription',
|
||||
});
|
||||
setSettingModalAtom({
|
||||
open: true,
|
||||
|
||||
@@ -108,17 +108,22 @@ const SubscriptionSettings = () => {
|
||||
|
||||
const openPlans = useCallback(
|
||||
(scrollAnchor?: string) => {
|
||||
mixpanel.track('Button', {
|
||||
resolve: 'ChangePlan',
|
||||
currentPlan: proSubscription?.plan,
|
||||
mixpanel.track('PlansViewed', {
|
||||
type: proSubscription?.plan,
|
||||
category: proSubscription?.recurring,
|
||||
// page:
|
||||
segment: 'settings panel',
|
||||
module: 'billing subscription list',
|
||||
control: 'change plan button',
|
||||
});
|
||||
|
||||
setOpenSettingModalAtom({
|
||||
open: true,
|
||||
activeTab: 'plans',
|
||||
scrollAnchor: scrollAnchor,
|
||||
});
|
||||
},
|
||||
[proSubscription?.plan, setOpenSettingModalAtom]
|
||||
[proSubscription?.plan, proSubscription?.recurring, setOpenSettingModalAtom]
|
||||
);
|
||||
const gotoCloudPlansSetting = useCallback(() => openPlans(), [openPlans]);
|
||||
const gotoAiPlanSetting = useCallback(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Button, type ButtonProps } from '@affine/component';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import { mixpanel, popupWindow } from '@affine/core/utils';
|
||||
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
@@ -42,6 +42,10 @@ export const AISubscribe = ({ ...btnProps }: AISubscribeProps) => {
|
||||
|
||||
const subscribe = useAsyncCallback(async () => {
|
||||
setMutating(true);
|
||||
mixpanel.track('plan upgrade started', {
|
||||
category: SubscriptionRecurring.Yearly,
|
||||
type: SubscriptionPlan.AI,
|
||||
});
|
||||
try {
|
||||
const session = await subscriptionService.createCheckoutSession({
|
||||
recurring: SubscriptionRecurring.Yearly,
|
||||
|
||||
@@ -115,15 +115,22 @@ export const SettingSidebar = ({
|
||||
const loginStatus = useLiveData(useService(AuthService).session.status$);
|
||||
const generalList = useGeneralSettingList();
|
||||
const onAccountSettingClick = useCallback(() => {
|
||||
mixpanel.track('Button', {
|
||||
resolve: 'AccountSetting',
|
||||
mixpanel.track('AccountSettingsViewed', {
|
||||
// page:
|
||||
segment: 'settings panel',
|
||||
module: 'settings menu',
|
||||
control: 'menu item',
|
||||
});
|
||||
onTabChange('account', null);
|
||||
}, [onTabChange]);
|
||||
const onWorkspaceSettingClick = useCallback(
|
||||
(subTab: WorkspaceSubTab, workspaceMetadata: WorkspaceMetadata) => {
|
||||
mixpanel.track('Button', {
|
||||
resolve: 'WorkspaceSetting',
|
||||
mixpanel.track(`view workspace setting`, {
|
||||
// page:
|
||||
segment: 'settings panel',
|
||||
module: 'settings menu',
|
||||
control: 'menu item',
|
||||
type: subTab,
|
||||
workspaceId: workspaceMetadata.id,
|
||||
});
|
||||
onTabChange(`workspace:${subTab}`, workspaceMetadata);
|
||||
@@ -148,9 +155,21 @@ export const SettingSidebar = ({
|
||||
key={key}
|
||||
title={title}
|
||||
onClick={() => {
|
||||
mixpanel.track('Button', {
|
||||
resolve: key,
|
||||
});
|
||||
if (key === 'billing') {
|
||||
mixpanel.track('BillingViewed', {
|
||||
// page:
|
||||
segment: 'settings panel',
|
||||
module: 'settings menu',
|
||||
control: 'menu item',
|
||||
});
|
||||
} else if (key === 'plans') {
|
||||
mixpanel.track('PlansViewed', {
|
||||
// page:
|
||||
segment: 'settings panel',
|
||||
module: 'settings menu',
|
||||
control: 'menu item',
|
||||
});
|
||||
}
|
||||
onTabChange(key, null);
|
||||
}}
|
||||
data-testid={testId}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { useMembers } from '@affine/core/hooks/affine/use-members';
|
||||
import { useRevokeMemberPermission } from '@affine/core/hooks/affine/use-revoke-member-permission';
|
||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { Permission } from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
@@ -144,6 +145,12 @@ export const CloudWorkspaceMembersPanel = () => {
|
||||
open: true,
|
||||
activeTab: 'plans',
|
||||
});
|
||||
mixpanel.track('PlansViewed', {
|
||||
// page:
|
||||
segment: 'settings panel',
|
||||
module: 'workspace setting',
|
||||
control: 'invite member',
|
||||
});
|
||||
}, [setSettingModalAtom]);
|
||||
|
||||
const listContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Button } from '@affine/component/ui/button';
|
||||
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { ShareService } from '@affine/core/modules/share-doc';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { PublicPageMode } from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
@@ -101,6 +102,12 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
||||
await shareService.share.enableShare(
|
||||
mode === 'edgeless' ? PublicPageMode.Edgeless : PublicPageMode.Page
|
||||
);
|
||||
mixpanel.track('ShareCreated', {
|
||||
segment: 'sharing panel',
|
||||
module: 'public share',
|
||||
control: 'share panel',
|
||||
type: mode,
|
||||
});
|
||||
notify.success({
|
||||
title:
|
||||
t[
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { toast } from '@affine/component';
|
||||
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
@@ -52,10 +53,14 @@ export const useSharingUrl = ({
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
mixpanel.track('ShareLinkCopied', {
|
||||
module: urlType === 'share' ? 'public share' : 'private share',
|
||||
type: 'link',
|
||||
});
|
||||
} else {
|
||||
toast('Network not available');
|
||||
}
|
||||
}, [sharingUrl, t]);
|
||||
}, [sharingUrl, t, urlType]);
|
||||
|
||||
return {
|
||||
sharingUrl,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { notify } from '@affine/component';
|
||||
import { authAtom, openSettingModalAtom } from '@affine/core/atoms';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { getBaseUrl } from '@affine/graphql';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { UnauthorizedError } from '@blocksuite/blocks';
|
||||
@@ -345,6 +346,11 @@ Could you make a new website based on these notes and send back just the html fi
|
||||
getCurrentStore().set(openSettingModalAtom, {
|
||||
activeTab: 'billing',
|
||||
open: true,
|
||||
scrollAnchor: 'aiPricingPlan',
|
||||
});
|
||||
mixpanel.track('PlansViewed', {
|
||||
segment: 'payment wall',
|
||||
category: 'payment wall ai action count',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import { Export, MoveToTrash } from '@affine/core/components/page-list';
|
||||
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
||||
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
||||
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
@@ -97,8 +99,34 @@ export const PageHeaderMenuButton = ({
|
||||
|
||||
const handleDuplicate = useCallback(() => {
|
||||
duplicate(pageId);
|
||||
mixpanel.track('DocCreated', {
|
||||
segment: 'editor header',
|
||||
module: 'header menu',
|
||||
control: 'copy doc',
|
||||
type: 'doc duplicate',
|
||||
category: 'doc',
|
||||
});
|
||||
}, [duplicate, pageId]);
|
||||
|
||||
const onImportFile = useAsyncCallback(async () => {
|
||||
const options = await importFile();
|
||||
if (options.isWorkspaceFile) {
|
||||
mixpanel.track('WorkspaceCreated', {
|
||||
segment: 'editor header',
|
||||
module: 'header menu',
|
||||
control: 'import button',
|
||||
type: 'imported workspace',
|
||||
});
|
||||
} else {
|
||||
mixpanel.track('DocCreated', {
|
||||
segment: 'editor header',
|
||||
module: 'header menu',
|
||||
control: 'import button',
|
||||
type: 'imported doc',
|
||||
});
|
||||
}
|
||||
}, [importFile]);
|
||||
|
||||
const EditMenu = (
|
||||
<>
|
||||
{!isJournal && (
|
||||
@@ -179,7 +207,7 @@ export const PageHeaderMenuButton = ({
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-import"
|
||||
onSelect={importFile}
|
||||
onSelect={onImportFile}
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['Import']()}
|
||||
|
||||
@@ -36,30 +36,47 @@ export const usePageHelper = (docCollection: DocCollection) => {
|
||||
return createPageAndOpen('edgeless');
|
||||
}, [createPageAndOpen]);
|
||||
|
||||
const importFileAndOpen = useAsyncCallback(async () => {
|
||||
const { showImportModal } = await import('@blocksuite/blocks');
|
||||
const onSuccess = (
|
||||
pageIds: string[],
|
||||
options: { isWorkspaceFile: boolean; importedCount: number }
|
||||
) => {
|
||||
toast(
|
||||
`Successfully imported ${options.importedCount} Page${
|
||||
options.importedCount > 1 ? 's' : ''
|
||||
}.`
|
||||
);
|
||||
if (options.isWorkspaceFile) {
|
||||
jumpToSubPath(docCollection.id, WorkspaceSubPath.ALL);
|
||||
return;
|
||||
}
|
||||
const importFileAndOpen = useMemo(
|
||||
() => async () => {
|
||||
const { showImportModal } = await import('@blocksuite/blocks');
|
||||
const { promise, resolve, reject } =
|
||||
Promise.withResolvers<
|
||||
Parameters<
|
||||
NonNullable<Parameters<typeof showImportModal>[0]['onSuccess']>
|
||||
>[1]
|
||||
>();
|
||||
const onSuccess = (
|
||||
pageIds: string[],
|
||||
options: { isWorkspaceFile: boolean; importedCount: number }
|
||||
) => {
|
||||
resolve(options);
|
||||
toast(
|
||||
`Successfully imported ${options.importedCount} Page${
|
||||
options.importedCount > 1 ? 's' : ''
|
||||
}.`
|
||||
);
|
||||
if (options.isWorkspaceFile) {
|
||||
jumpToSubPath(docCollection.id, WorkspaceSubPath.ALL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pageIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
const pageId = pageIds[0];
|
||||
openPage(docCollection.id, pageId);
|
||||
};
|
||||
showImportModal({ collection: docCollection, onSuccess });
|
||||
}, [docCollection, openPage, jumpToSubPath]);
|
||||
if (pageIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
const pageId = pageIds[0];
|
||||
openPage(docCollection.id, pageId);
|
||||
};
|
||||
showImportModal({
|
||||
collection: docCollection,
|
||||
onSuccess,
|
||||
onFail: message => {
|
||||
reject(new Error(message));
|
||||
},
|
||||
});
|
||||
return await promise;
|
||||
},
|
||||
[docCollection, openPage, jumpToSubPath]
|
||||
);
|
||||
|
||||
const createLinkedPageAndOpen = useAsyncCallback(
|
||||
async (pageId: string) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DropdownButton, Menu } from '@affine/component';
|
||||
import { BlockCard } from '@affine/component/card/block-card';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
@@ -69,11 +70,27 @@ export const NewPageButton = ({
|
||||
const handleCreateNewPage = useCallback(() => {
|
||||
createNewPage();
|
||||
setOpen(false);
|
||||
mixpanel.track('DocCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'all doc',
|
||||
module: 'doc list header',
|
||||
control: 'new doc button',
|
||||
type: 'doc',
|
||||
category: 'page',
|
||||
});
|
||||
}, [createNewPage]);
|
||||
|
||||
const handleCreateNewEdgeless = useCallback(() => {
|
||||
createNewEdgeless();
|
||||
setOpen(false);
|
||||
mixpanel.track('DocCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'all doc',
|
||||
module: 'doc list header',
|
||||
control: 'new whiteboard button',
|
||||
type: 'doc',
|
||||
category: 'whiteboard',
|
||||
});
|
||||
}, [createNewEdgeless]);
|
||||
|
||||
const handleImportFile = useCallback(() => {
|
||||
@@ -104,10 +121,7 @@ export const NewPageButton = ({
|
||||
>
|
||||
<DropdownButton
|
||||
size={size}
|
||||
onClick={useCallback(() => {
|
||||
createNewPage();
|
||||
setOpen(false);
|
||||
}, [createNewPage])}
|
||||
onClick={handleCreateNewPage}
|
||||
onClickDropDown={useCallback(() => setOpen(open => !open), [])}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
||||
import type { Tag } from '@affine/core/modules/tag';
|
||||
import { TagService } from '@affine/core/modules/tag';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import type { Collection } from '@affine/env/filter';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
@@ -46,6 +47,28 @@ export const PageListHeader = () => {
|
||||
return t['com.affine.all-pages.header']();
|
||||
}, [t]);
|
||||
|
||||
const onImportFile = useAsyncCallback(async () => {
|
||||
const options = await importFile();
|
||||
if (options.isWorkspaceFile) {
|
||||
mixpanel.track('WorkspaceCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'all doc',
|
||||
module: 'doc list header',
|
||||
control: 'import button',
|
||||
type: 'imported workspace',
|
||||
});
|
||||
} else {
|
||||
mixpanel.track('DocCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'all doc',
|
||||
module: 'doc list header',
|
||||
control: 'import button',
|
||||
type: 'imported doc',
|
||||
// category
|
||||
});
|
||||
}
|
||||
}, [importFile]);
|
||||
|
||||
return (
|
||||
<div className={styles.docListHeader}>
|
||||
<div className={styles.docListHeaderTitle}>{title}</div>
|
||||
@@ -54,7 +77,7 @@ export const PageListHeader = () => {
|
||||
testId="new-page-button-trigger"
|
||||
onCreateEdgeless={createEdgeless}
|
||||
onCreatePage={createPage}
|
||||
onImportFile={importFile}
|
||||
onImportFile={onImportFile}
|
||||
>
|
||||
<div className={styles.buttonText}>{t['New Page']()}</div>
|
||||
</PageListNewPageButton>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { TagService } from '@affine/core/modules/tag';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons';
|
||||
import type { DocMeta } from '@blocksuite/store';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { LiveData, useLiveData, useService } from '@toeverything/infra';
|
||||
import { type ReactNode, useMemo } from 'react';
|
||||
|
||||
import * as styles from './group-definitions.css';
|
||||
@@ -128,7 +128,17 @@ const GroupTagLabel = ({ tag, count }: { tag: Tag; count: number }) => {
|
||||
};
|
||||
export const useTagGroupDefinitions = (): ItemGroupDefinition<ListItem>[] => {
|
||||
const tagList = useService(TagService).tagList;
|
||||
const tags = useLiveData(tagList.tags$);
|
||||
const sortedTagsLiveData$ = useMemo(
|
||||
() =>
|
||||
LiveData.computed(get =>
|
||||
get(tagList.tags$).sort((a, b) =>
|
||||
get(a.value$).localeCompare(get(b.value$))
|
||||
)
|
||||
),
|
||||
[tagList.tags$]
|
||||
);
|
||||
const tags = useLiveData(sortedTagsLiveData$);
|
||||
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const untagged = useMemo(
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-sui
|
||||
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
||||
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
@@ -96,6 +97,13 @@ export const PageOperationCell = ({
|
||||
|
||||
const onDuplicate = useCallback(() => {
|
||||
duplicate(page.id, false);
|
||||
mixpanel.track('DocCreated', {
|
||||
segment: 'all doc',
|
||||
module: 'doc item menu',
|
||||
control: 'copy doc',
|
||||
type: 'doc duplicate',
|
||||
category: 'doc',
|
||||
});
|
||||
}, [duplicate, page.id]);
|
||||
|
||||
const OperationMenu = (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useGetDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite
|
||||
import { useJournalHelper } from '@affine/core/hooks/use-journal';
|
||||
import { CollectionService } from '@affine/core/modules/collection';
|
||||
import { WorkspaceSubPath } from '@affine/core/shared';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import type { Collection } from '@affine/env/filter';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
@@ -235,6 +236,9 @@ export const usePageCommands = () => {
|
||||
page.id,
|
||||
blockId
|
||||
);
|
||||
mixpanel.track('AppendToJournal', {
|
||||
control: 'cmdk',
|
||||
});
|
||||
},
|
||||
icon: <TodayIcon />,
|
||||
});
|
||||
@@ -250,6 +254,10 @@ export const usePageCommands = () => {
|
||||
const page = pageHelper.createPage();
|
||||
page.load();
|
||||
pageMetaHelper.setDocTitle(page.id, query);
|
||||
mixpanel.track('DocCreated', {
|
||||
control: 'cmdk',
|
||||
type: 'doc',
|
||||
});
|
||||
},
|
||||
icon: <PageIcon />,
|
||||
});
|
||||
@@ -265,6 +273,10 @@ export const usePageCommands = () => {
|
||||
const page = pageHelper.createEdgeless();
|
||||
page.load();
|
||||
pageMetaHelper.setDocTitle(page.id, query);
|
||||
mixpanel.track('DocCreated', {
|
||||
control: 'cmdk',
|
||||
type: 'whiteboard',
|
||||
});
|
||||
},
|
||||
icon: <EdgelessIcon />,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IconButton } from '@affine/component/ui/button';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { PlusIcon } from '@blocksuite/icons';
|
||||
import type { DocCollection } from '@blocksuite/store';
|
||||
import { useService } from '@toeverything/infra';
|
||||
@@ -24,10 +25,26 @@ export const AddFavouriteButton = ({
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
createLinkedPage(pageId);
|
||||
mixpanel.track('DocCreated', {
|
||||
// page:
|
||||
segment: 'all doc',
|
||||
module: 'favorite',
|
||||
control: 'new fav sub doc',
|
||||
type: 'doc',
|
||||
category: 'page',
|
||||
});
|
||||
} else {
|
||||
const page = createPage();
|
||||
page.load();
|
||||
favAdapter.set(page.id, 'doc', true);
|
||||
mixpanel.track('DocCreated', {
|
||||
// page:
|
||||
segment: 'all doc',
|
||||
module: 'favorite',
|
||||
control: 'new fav doc',
|
||||
type: 'doc',
|
||||
category: 'page',
|
||||
});
|
||||
}
|
||||
},
|
||||
[pageId, createLinkedPage, createPage, favAdapter]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ImportIcon } from '@blocksuite/icons';
|
||||
|
||||
@@ -8,8 +10,31 @@ import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
||||
const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const { importFile } = usePageHelper(docCollection);
|
||||
|
||||
const onImportFile = useAsyncCallback(async () => {
|
||||
const options = await importFile();
|
||||
if (options.isWorkspaceFile) {
|
||||
mixpanel.track('WorkspaceCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'navigation panel',
|
||||
module: 'doc list header',
|
||||
control: 'import button',
|
||||
type: 'imported workspace',
|
||||
});
|
||||
} else {
|
||||
mixpanel.track('DocCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'navigation panel',
|
||||
module: 'doc list header',
|
||||
control: 'import button',
|
||||
type: 'imported doc',
|
||||
// category
|
||||
});
|
||||
}
|
||||
}, [importFile]);
|
||||
|
||||
return (
|
||||
<MenuItem icon={<ImportIcon />} onClick={importFile}>
|
||||
<MenuItem icon={<ImportIcon />} onClick={onImportFile}>
|
||||
{t['Import']()}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AnimatedDeleteIcon } from '@affine/component';
|
||||
import { getDNDId } from '@affine/core/hooks/affine/use-global-dnd-helper';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { CollectionService } from '@affine/core/modules/collection';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { apis, events } from '@affine/electron-api';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { FolderIcon, SettingsIcon } from '@blocksuite/icons';
|
||||
@@ -106,11 +107,23 @@ export const RootAppSidebar = ({
|
||||
)
|
||||
);
|
||||
|
||||
const allPageActive = currentPath === '/all';
|
||||
|
||||
const trashActive = currentPath === '/trash';
|
||||
|
||||
const onClickNewPage = useAsyncCallback(async () => {
|
||||
const page = createPage();
|
||||
page.load();
|
||||
openPage(page.id);
|
||||
}, [createPage, openPage]);
|
||||
mixpanel.track('DocCreated', {
|
||||
page: allPageActive ? 'all' : trashActive ? 'trash' : 'other',
|
||||
segment: 'navigation panel',
|
||||
module: 'bottom button',
|
||||
control: 'new doc button',
|
||||
category: 'page',
|
||||
type: 'doc',
|
||||
});
|
||||
}, [allPageActive, createPage, openPage, trashActive]);
|
||||
|
||||
const { trashModal, setTrashModal, handleOnConfirm } =
|
||||
useTrashModalHelper(docCollection);
|
||||
@@ -166,10 +179,6 @@ export const RootAppSidebar = ({
|
||||
});
|
||||
}, [docCollection.id, collection, navigateHelper, open]);
|
||||
|
||||
const allPageActive = currentPath === '/all';
|
||||
|
||||
const trashActive = currentPath === '/trash';
|
||||
|
||||
return (
|
||||
<AppSidebar
|
||||
clientBorder={appSettings.clientBorder}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
openSettingModalAtom,
|
||||
openSignOutModalAtom,
|
||||
} from '@affine/core/atoms';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { AccountIcon, SignOutIcon } from '@blocksuite/icons';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
@@ -79,6 +80,12 @@ const AccountMenu = () => {
|
||||
const setOpenSignOutModalAtom = useSetAtom(openSignOutModalAtom);
|
||||
|
||||
const onOpenAccountSetting = useCallback(() => {
|
||||
mixpanel.track('AccountSettingsViewed', {
|
||||
// page:
|
||||
segment: 'navigation panel',
|
||||
module: 'profile and badge',
|
||||
control: 'profile and email',
|
||||
});
|
||||
setSettingModalAtom(prev => ({
|
||||
...prev,
|
||||
open: true,
|
||||
|
||||
@@ -23,10 +23,12 @@ export const AppContainer = ({
|
||||
useNoisyBackground,
|
||||
useBlurBackground,
|
||||
children,
|
||||
...rest
|
||||
}: WorkspaceRootProps) => {
|
||||
const noisyBackground = useNoisyBackground && environment.isDesktop;
|
||||
return (
|
||||
<div
|
||||
{...rest}
|
||||
className={clsx(appStyle, {
|
||||
'noisy-background': noisyBackground,
|
||||
'blur-background': environment.isDesktop && useBlurBackground,
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
pushGlobalLoadingEventAtom,
|
||||
resolveGlobalLoadingEventAtom,
|
||||
} from '@affine/component/global-loading';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { apis } from '@affine/electron-api';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
|
||||
@@ -25,6 +26,11 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
|
||||
if (editorRoot) {
|
||||
pageService = editorRoot.spec.getService<PageRootService>('affine:page');
|
||||
}
|
||||
mixpanel.track('ShareCreated', {
|
||||
type,
|
||||
segment: 'sharing panel',
|
||||
module: 'export share',
|
||||
});
|
||||
switch (type) {
|
||||
case 'html':
|
||||
await HtmlTransformer.exportDoc(page);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { toast } from '@affine/component';
|
||||
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
@@ -141,6 +142,11 @@ export function useRegisterBlocksuiteEditorCommands() {
|
||||
label: t['com.affine.header.option.duplicate'](),
|
||||
run() {
|
||||
duplicate(docId);
|
||||
mixpanel.track('DocCreated', {
|
||||
control: 'cmdk',
|
||||
type: 'doc duplicate',
|
||||
category: 'doc',
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
} from '../hooks/affine/use-global-dnd-helper';
|
||||
import { useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||
import { useRegisterWorkspaceCommands } from '../hooks/use-register-workspace-commands';
|
||||
import { WorkbenchService } from '../modules/workbench';
|
||||
import {
|
||||
AllWorkspaceModals,
|
||||
CurrentWorkspaceModals,
|
||||
@@ -62,7 +63,6 @@ export const QuickSearch = () => {
|
||||
|
||||
const onToggleQuickSearch = useCallback(
|
||||
(open: boolean) => {
|
||||
mixpanel.track('QuickSearch', { open });
|
||||
setOpenQuickSearchModalAtom(open);
|
||||
},
|
||||
[setOpenQuickSearchModalAtom]
|
||||
@@ -113,6 +113,14 @@ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
|
||||
const upgrading = useLiveData(currentWorkspace.upgrade.upgrading$);
|
||||
const needUpgrade = useLiveData(currentWorkspace.upgrade.needUpgrade$);
|
||||
|
||||
const workbench = useService(WorkbenchService).workbench;
|
||||
|
||||
const basename = useLiveData(workbench.basename$);
|
||||
|
||||
const currentPath = useLiveData(
|
||||
workbench.location$.map(location => basename + location.pathname)
|
||||
);
|
||||
|
||||
useRegisterWorkspaceCommands();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -143,6 +151,10 @@ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
|
||||
const [, setOpenQuickSearchModalAtom] = useAtom(openQuickSearchModalAtom);
|
||||
const handleOpenQuickSearchModal = useCallback(() => {
|
||||
setOpenQuickSearchModalAtom(true);
|
||||
mixpanel.track('QuickSearchOpened', {
|
||||
segment: 'navigation panel',
|
||||
control: 'search button',
|
||||
});
|
||||
}, [setOpenQuickSearchModalAtom]);
|
||||
|
||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
@@ -152,6 +164,12 @@ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
|
||||
activeTab: 'appearance',
|
||||
open: true,
|
||||
});
|
||||
mixpanel.track('SettingsViewed', {
|
||||
// page:
|
||||
segment: 'navigation panel',
|
||||
module: 'general list',
|
||||
control: 'settings button',
|
||||
});
|
||||
}, [setOpenSettingModalAtom]);
|
||||
|
||||
const resizing = useAtomValue(appSidebarResizingAtom);
|
||||
@@ -171,7 +189,7 @@ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
|
||||
<>
|
||||
{/* This DndContext is used for drag page from all-pages list into a folder in sidebar */}
|
||||
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
|
||||
<AppContainer resizing={resizing}>
|
||||
<AppContainer data-current-path={currentPath} resizing={resizing}>
|
||||
<Suspense fallback={<AppSidebarFallback />}>
|
||||
<RootAppSidebar
|
||||
isPublicWorkspace={false}
|
||||
|
||||
@@ -19,9 +19,14 @@ const EditorChatPanel = ({ editor }: SidebarTabProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
editor.host.spec.getService('affine:page').slots.docLinkClicked.on(() => {
|
||||
const pageService = editor.host.spec.getService('affine:page');
|
||||
|
||||
pageService.slots.docLinkClicked.on(() => {
|
||||
(chatPanelRef.current as ChatPanel).doc = editor.doc;
|
||||
});
|
||||
pageService.slots.editorModeSwitch.on(() => {
|
||||
(chatPanelRef.current as ChatPanel).host = editor.host;
|
||||
});
|
||||
}, [editor]);
|
||||
|
||||
if (!editor) {
|
||||
@@ -32,11 +37,9 @@ const EditorChatPanel = ({ editor }: SidebarTabProps) => {
|
||||
chatPanelRef.current = new ChatPanel();
|
||||
}
|
||||
|
||||
if (editor !== chatPanelRef.current?.editor) {
|
||||
(chatPanelRef.current as ChatPanel).editor = editor;
|
||||
(chatPanelRef.current as ChatPanel).doc = editor.doc;
|
||||
// (copilotPanelRef.current as CopilotPanel).fitPadding = [20, 20, 20, 20];
|
||||
}
|
||||
(chatPanelRef.current as ChatPanel).host = editor.host;
|
||||
(chatPanelRef.current as ChatPanel).doc = editor.doc;
|
||||
// (copilotPanelRef.current as CopilotPanel).fitPadding = [20, 20, 20, 20];
|
||||
|
||||
return <div className={styles.root} ref={onRefChange} />;
|
||||
};
|
||||
|
||||
@@ -7,10 +7,12 @@ import {
|
||||
type AuthAccountInfo,
|
||||
type AuthService,
|
||||
} from '../../cloud';
|
||||
import { AccountLoggedOut } from '../../cloud/services/auth';
|
||||
import { UserQuotaChanged } from '../../cloud/services/user-quota';
|
||||
|
||||
@OnEvent(ApplicationStarted, e => e.onApplicationStart)
|
||||
@OnEvent(AccountChanged, e => e.onAccountChanged)
|
||||
@OnEvent(AccountChanged, e => e.updateIdentity)
|
||||
@OnEvent(AccountLoggedOut, e => e.onAccountLoggedOut)
|
||||
@OnEvent(UserQuotaChanged, e => e.onUserQuotaChanged)
|
||||
export class TelemetryService extends Service {
|
||||
private prevQuota: NonNullable<QuotaQuery['currentUser']>['quota'] | null =
|
||||
@@ -26,23 +28,32 @@ export class TelemetryService extends Service {
|
||||
track_pageview: true,
|
||||
persistence: 'localStorage',
|
||||
});
|
||||
}
|
||||
const account = this.auth.session.account$.value;
|
||||
this.onAccountChanged(account);
|
||||
}
|
||||
|
||||
onAccountChanged(account: AuthAccountInfo | null) {
|
||||
if (account === null) {
|
||||
mixpanel.reset();
|
||||
} else {
|
||||
mixpanel.reset();
|
||||
mixpanel.identify(account.id);
|
||||
mixpanel.people.set({
|
||||
$email: account.email,
|
||||
$name: account.label,
|
||||
$avatar: account.avatar,
|
||||
mixpanel.register({
|
||||
appVersion: runtimeConfig.appVersion,
|
||||
environment: runtimeConfig.appBuildType,
|
||||
editorVersion: runtimeConfig.editorVersion,
|
||||
isSelfHosted: Boolean(runtimeConfig.isSelfHosted),
|
||||
isDesktop: environment.isDesktop,
|
||||
});
|
||||
}
|
||||
const account = this.auth.session.account$.value;
|
||||
this.updateIdentity(account);
|
||||
}
|
||||
|
||||
updateIdentity(account: AuthAccountInfo | null) {
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
mixpanel.identify(account.id);
|
||||
mixpanel.people.set({
|
||||
$email: account.email,
|
||||
$name: account.label,
|
||||
$avatar: account.avatar,
|
||||
});
|
||||
}
|
||||
|
||||
onAccountLoggedOut() {
|
||||
mixpanel.reset();
|
||||
}
|
||||
|
||||
onUserQuotaChanged(quota: NonNullable<QuotaQuery['currentUser']>['quota']) {
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
import { Service } from '@toeverything/infra';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { createEvent, Service } from '@toeverything/infra';
|
||||
import { combineLatest, distinctUntilChanged, map, skip } from 'rxjs';
|
||||
|
||||
import { Workbench } from '../entities/workbench';
|
||||
|
||||
export const WorkbenchLocationChanged = createEvent<string>(
|
||||
'WorkbenchLocationChanged'
|
||||
);
|
||||
|
||||
export class WorkbenchService extends Service {
|
||||
constructor() {
|
||||
super();
|
||||
combineLatest([this.workbench.location$, this.workbench.basename$])
|
||||
.pipe(
|
||||
map(([location, basename]) => basename + location.pathname),
|
||||
distinctUntilChanged(),
|
||||
skip(1)
|
||||
)
|
||||
.subscribe(newLocation => {
|
||||
this.eventBus.root.emit(WorkbenchLocationChanged, newLocation);
|
||||
mixpanel.track_pageview({
|
||||
location: newLocation,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
workbench = this.framework.createEntity(Workbench);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { EMPTY, mergeMap, switchMap } from 'rxjs';
|
||||
|
||||
import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||
import { AuthService, SubscriptionService } from '../modules/cloud';
|
||||
import { mixpanel } from '../utils';
|
||||
import { container } from './subscribe.css';
|
||||
|
||||
export const Component = () => {
|
||||
@@ -68,6 +69,16 @@ export const Component = () => {
|
||||
});
|
||||
setMessage('Redirecting...');
|
||||
location.href = checkout;
|
||||
mixpanel.track('PlanChangeSucceeded', {
|
||||
type: plan,
|
||||
category: recurring,
|
||||
});
|
||||
if (plan) {
|
||||
mixpanel.people.set({
|
||||
[SubscriptionPlan.AI === plan ? 'ai plan' : plan]: plan,
|
||||
recurring: recurring,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError('Something went wrong. Please try again.');
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from '@affine/core/components/page-list';
|
||||
import { Header } from '@affine/core/components/pure/header';
|
||||
import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import type { Filter } from '@affine/env/filter';
|
||||
import { PlusIcon } from '@blocksuite/icons';
|
||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||
@@ -27,6 +29,28 @@ export const AllPageHeader = ({
|
||||
workspace.docCollection
|
||||
);
|
||||
|
||||
const onImportFile = useAsyncCallback(async () => {
|
||||
const options = await importFile();
|
||||
if (options.isWorkspaceFile) {
|
||||
mixpanel.track('WorkspaceCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'all page',
|
||||
module: 'doc list header',
|
||||
control: 'import button',
|
||||
type: 'imported workspace',
|
||||
});
|
||||
} else {
|
||||
mixpanel.track('DocCreated', {
|
||||
page: 'doc library',
|
||||
segment: 'all page',
|
||||
module: 'doc list header',
|
||||
control: 'import button',
|
||||
type: 'imported doc',
|
||||
// category
|
||||
});
|
||||
}
|
||||
}, [importFile]);
|
||||
|
||||
return (
|
||||
<Header
|
||||
left={
|
||||
@@ -46,7 +70,7 @@ export const AllPageHeader = ({
|
||||
)}
|
||||
onCreateEdgeless={createEdgeless}
|
||||
onCreatePage={createPage}
|
||||
onImportFile={importFile}
|
||||
onImportFile={onImportFile}
|
||||
>
|
||||
<PlusIcon />
|
||||
</PageListNewPageButton>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IconButton } from '@affine/component';
|
||||
import { PageDisplayMenu } from '@affine/core/components/page-list';
|
||||
import { Header } from '@affine/core/components/pure/header';
|
||||
import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab';
|
||||
import { PlusIcon } from '@blocksuite/icons';
|
||||
@@ -16,16 +17,19 @@ export const CollectionDetailHeader = ({
|
||||
return (
|
||||
<Header
|
||||
right={
|
||||
<IconButton
|
||||
type="default"
|
||||
icon={<PlusIcon fontSize={16} />}
|
||||
onClick={onCreate}
|
||||
className={clsx(
|
||||
styles.headerCreateNewButton,
|
||||
styles.headerCreateNewCollectionIconButton,
|
||||
!showCreateNew && styles.headerCreateNewButtonHidden
|
||||
)}
|
||||
/>
|
||||
<>
|
||||
<IconButton
|
||||
type="default"
|
||||
icon={<PlusIcon fontSize={16} />}
|
||||
onClick={onCreate}
|
||||
className={clsx(
|
||||
styles.headerCreateNewButton,
|
||||
styles.headerCreateNewCollectionIconButton,
|
||||
!showCreateNew && styles.headerCreateNewButtonHidden
|
||||
)}
|
||||
/>
|
||||
<PageDisplayMenu />
|
||||
</>
|
||||
}
|
||||
center={<WorkspaceModeFilterTab activeFilter={'collections'} />}
|
||||
/>
|
||||
|
||||
@@ -32,3 +32,7 @@ export const affineDocViewport = style({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const scrollbar = style({
|
||||
marginRight: '4px',
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Scrollable } from '@affine/component';
|
||||
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
||||
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||
import type { PageRootService } from '@blocksuite/blocks';
|
||||
import {
|
||||
BookmarkService,
|
||||
@@ -90,6 +91,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
const rightSidebar = useService(RightSidebarService).rightSidebar;
|
||||
const docCollection = workspace.docCollection;
|
||||
const mode = useLiveData(doc.mode$);
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
|
||||
const isActiveView = useIsActiveView();
|
||||
// TODO: remove jotai here
|
||||
@@ -252,7 +254,11 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
docCollection={docCollection}
|
||||
/>
|
||||
</Scrollable.Viewport>
|
||||
<Scrollable.Scrollbar />
|
||||
<Scrollable.Scrollbar
|
||||
className={clsx({
|
||||
[styles.scrollbar]: !appSettings.clientBorder,
|
||||
})}
|
||||
/>
|
||||
</Scrollable.Root>
|
||||
</AffineErrorBoundary>
|
||||
{isInTrash ? <TrashPageFooter /> : null}
|
||||
|
||||
@@ -66,12 +66,12 @@ export const Component = (): ReactElement => {
|
||||
},
|
||||
})
|
||||
);
|
||||
window.exportWorkspaceSnapshot = async () => {
|
||||
window.exportWorkspaceSnapshot = async (docs?: string[]) => {
|
||||
const zip = await ZipTransformer.exportDocs(
|
||||
workspace.docCollection,
|
||||
Array.from(workspace.docCollection.docs.values()).map(collection =>
|
||||
collection.getDoc()
|
||||
)
|
||||
Array.from(workspace.docCollection.docs.values())
|
||||
.filter(doc => (docs ? docs.includes(doc.id) : true))
|
||||
.map(doc => doc.getDoc())
|
||||
);
|
||||
const url = URL.createObjectURL(zip);
|
||||
// download url
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { PageDisplayMenu } from '@affine/core/components/page-list';
|
||||
import { Header } from '@affine/core/components/pure/header';
|
||||
import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab';
|
||||
|
||||
export const TagDetailHeader = () => {
|
||||
return <Header center={<WorkspaceModeFilterTab activeFilter={'tags'} />} />;
|
||||
return (
|
||||
<Header
|
||||
center={<WorkspaceModeFilterTab activeFilter={'tags'} />}
|
||||
right={<PageDisplayMenu />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,17 +5,13 @@ import {
|
||||
createBrowserRouter as reactRouterCreateBrowserRouter,
|
||||
Outlet,
|
||||
redirect,
|
||||
useLocation,
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
useNavigate,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { mixpanel } from './utils';
|
||||
|
||||
export const NavigateContext = createContext<NavigateFunction | null>(null);
|
||||
|
||||
function RootRouter() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [ready, setReady] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -23,16 +19,6 @@ function RootRouter() {
|
||||
setReady(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
mixpanel.track_pageview({
|
||||
page: location.pathname,
|
||||
appVersion: runtimeConfig.appVersion,
|
||||
environment: runtimeConfig.appBuildType,
|
||||
editorVersion: runtimeConfig.editorVersion,
|
||||
isSelfHosted: Boolean(runtimeConfig.isSelfHosted),
|
||||
isDesktop: environment.isDesktop,
|
||||
});
|
||||
}, [location]);
|
||||
return (
|
||||
ready && (
|
||||
<NavigateContext.Provider value={navigate}>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { appSettingAtom } from '@toeverything/infra';
|
||||
import { useAtomValue } from 'jotai/react';
|
||||
import mixpanel from 'mixpanel-browser';
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
import { mixpanel } from './utils';
|
||||
|
||||
export function Telemetry() {
|
||||
const settings = useAtomValue(appSettingAtom);
|
||||
useLayoutEffect(() => {
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
"@affine/env": "workspace:*",
|
||||
"@affine/i18n": "workspace:*",
|
||||
"@affine/native": "workspace:*",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/store": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@blocksuite/block-std": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/blocks": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@blocksuite/store": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@electron-forge/cli": "^7.3.0",
|
||||
"@electron-forge/core": "^7.3.0",
|
||||
"@electron-forge/core-utils": "^7.3.0",
|
||||
@@ -43,7 +43,7 @@
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.3.0",
|
||||
"@electron-forge/shared-types": "^7.3.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@pengx17/electron-forge-maker-appimage": "^1.2.1",
|
||||
"@pengx17/electron-forge-maker-appimage": "^1.2.0",
|
||||
"@sentry/electron": "^4.22.0",
|
||||
"@sentry/esbuild-plugin": "^2.16.1",
|
||||
"@sentry/react": "^7.109.0",
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import { parse } from 'node:url';
|
||||
|
||||
import { app, BrowserWindow, shell } from 'electron';
|
||||
|
||||
import { logger } from '../logger';
|
||||
|
||||
const redirectUri = 'https://affine.pro/client/auth-callback';
|
||||
|
||||
export const oauthEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${process.env.AFFINE_GOOGLE_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=code&scope=openid https://www.googleapis.com/auth/userinfo.email profile&access_type=offline&customParameters={"prompt":"select_account"}`;
|
||||
|
||||
const tokenEndpoint = 'https://oauth2.googleapis.com/token';
|
||||
|
||||
export const getExchangeTokenParams = (code: string) => {
|
||||
const postData = {
|
||||
code,
|
||||
client_id: process.env.AFFINE_GOOGLE_CLIENT_ID || '',
|
||||
client_secret: process.env.AFFINE_GOOGLE_CLIENT_SECRET || '',
|
||||
redirect_uri: redirectUri,
|
||||
grant_type: 'authorization_code',
|
||||
};
|
||||
const requestInit: RequestInit = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams(postData).toString(),
|
||||
};
|
||||
return { requestInit, url: tokenEndpoint };
|
||||
};
|
||||
|
||||
export function getGoogleOauthCode() {
|
||||
return new Promise<ReturnType<typeof getExchangeTokenParams>>(
|
||||
(resolve, reject) => {
|
||||
shell.openExternal(oauthEndpoint).catch(e => {
|
||||
logger.error('Failed to open external url', e);
|
||||
reject(e);
|
||||
});
|
||||
const handleOpenUrl = (_: any, url: string) => {
|
||||
const mainWindow = BrowserWindow.getAllWindows().find(
|
||||
w => !w.isDestroyed()
|
||||
);
|
||||
const urlObj = parse(url.replace('??', '?'), true);
|
||||
if (!mainWindow || !url.startsWith('affine://auth-callback')) return;
|
||||
const code = urlObj.query['code'] as string;
|
||||
if (!code) return;
|
||||
|
||||
logger.info('google sign in code received from callback', code);
|
||||
|
||||
app.removeListener('open-url', handleOpenUrl);
|
||||
resolve(getExchangeTokenParams(code));
|
||||
};
|
||||
|
||||
app.on('open-url', handleOpenUrl);
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new Error('Timed out'));
|
||||
app.removeListener('open-url', handleOpenUrl);
|
||||
}, 30000);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import { getOnboardingWindow } from '../onboarding';
|
||||
import type { NamespaceHandlers } from '../type';
|
||||
import { launchStage } from '../windows-manager/stage';
|
||||
import { getChallengeResponse } from './challenge';
|
||||
import { getGoogleOauthCode } from './google-auth';
|
||||
|
||||
export let isOnline = true;
|
||||
|
||||
@@ -63,9 +62,6 @@ export const uiHandlers = {
|
||||
handleNetworkChange: async (_, _isOnline: boolean) => {
|
||||
isOnline = _isOnline;
|
||||
},
|
||||
getGoogleOauthCode: async () => {
|
||||
return getGoogleOauthCode();
|
||||
},
|
||||
getChallengeResponse: async (_, challenge: string) => {
|
||||
return getChallengeResponse(challenge);
|
||||
},
|
||||
|
||||
@@ -574,6 +574,7 @@
|
||||
"com.affine.cmdk.affine.navigation.goto-trash": "Go to Trash",
|
||||
"com.affine.cmdk.affine.navigation.goto-workspace": "Go to Workspace",
|
||||
"com.affine.cmdk.affine.navigation.open-settings": "Go to Settings",
|
||||
"com.affine.cmdk.affine.navigation.open-account-settings": "Go to Account Settings",
|
||||
"com.affine.cmdk.affine.new-edgeless-page": "New Edgeless",
|
||||
"com.affine.cmdk.affine.new-page": "New Doc",
|
||||
"com.affine.cmdk.affine.new-workspace": "New Workspace",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "3.0.0-alpha.54",
|
||||
"@napi-rs/cli": "3.0.0-alpha.55",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"ava": "^6.1.2",
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"devDependencies": {
|
||||
"@affine/env": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@aws-sdk/client-s3": "3.572.0",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405082235-4e0896c",
|
||||
"@aws-sdk/client-s3": "3.574.0",
|
||||
"@blocksuite/presets": "0.14.0-canary-202405100201-e591bb8",
|
||||
"@clack/core": "^0.3.4",
|
||||
"@clack/prompts": "^0.7.0",
|
||||
"@magic-works/i18n-codegen": "^0.6.0",
|
||||
|
||||
Reference in New Issue
Block a user