mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-23 20:18:42 +08:00
chore(electron): add telemetry events for enabling meetings (#11327)
fix AF-2436
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"sideEffects": "false",
|
"sideEffects": false,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/add": "^5.0.3",
|
"@graphql-codegen/add": "^5.0.3",
|
||||||
"@graphql-codegen/cli": "5.0.5",
|
"@graphql-codegen/cli": "5.0.5",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"@affine/electron-api": "workspace:*",
|
"@affine/electron-api": "workspace:*",
|
||||||
"@affine/i18n": "workspace:*",
|
"@affine/i18n": "workspace:*",
|
||||||
"@affine/nbstore": "workspace:*",
|
"@affine/nbstore": "workspace:*",
|
||||||
|
"@affine/track": "workspace:*",
|
||||||
"@blocksuite/affine": "workspace:*",
|
"@blocksuite/affine": "workspace:*",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@sentry/react": "^9.2.0",
|
"@sentry/react": "^9.2.0",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { WorkbenchService } from '@affine/core/modules/workbench';
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { apis, events } from '@affine/electron-api';
|
import { apis, events } from '@affine/electron-api';
|
||||||
import { i18nTime } from '@affine/i18n';
|
import { i18nTime } from '@affine/i18n';
|
||||||
|
import track from '@affine/track';
|
||||||
import type { AttachmentBlockModel } from '@blocksuite/affine/model';
|
import type { AttachmentBlockModel } from '@blocksuite/affine/model';
|
||||||
import { Text } from '@blocksuite/affine/store';
|
import { Text } from '@blocksuite/affine/store';
|
||||||
import type { BlobEngine } from '@blocksuite/affine/sync';
|
import type { BlobEngine } from '@blocksuite/affine/sync';
|
||||||
@@ -114,9 +115,18 @@ export function setupRecordingEvents(frameworkProvider: FrameworkProvider) {
|
|||||||
using audioAttachment = workspace.scope
|
using audioAttachment = workspace.scope
|
||||||
.get(AudioAttachmentService)
|
.get(AudioAttachmentService)
|
||||||
.get(model);
|
.get(model);
|
||||||
audioAttachment?.obj.transcribe().catch(err => {
|
audioAttachment?.obj
|
||||||
logger.error('Failed to transcribe recording', err);
|
.transcribe()
|
||||||
});
|
.then(() => {
|
||||||
|
track.doc.editor.audioBlock.transcribeRecording({
|
||||||
|
type: 'Meeting record',
|
||||||
|
method: 'success',
|
||||||
|
option: 'Auto transcribing',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error('Failed to transcribe recording', err);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No attachment model found');
|
throw new Error('No attachment model found');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { appIconMap } from '@affine/core/utils';
|
|||||||
import { encodeRawBufferToOpus } from '@affine/core/utils/webm-encoding';
|
import { encodeRawBufferToOpus } from '@affine/core/utils/webm-encoding';
|
||||||
import { apis, events } from '@affine/electron-api';
|
import { apis, events } from '@affine/electron-api';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import track from '@affine/track';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
@@ -81,12 +82,20 @@ export function Recording() {
|
|||||||
|
|
||||||
const handleDismiss = useAsyncCallback(async () => {
|
const handleDismiss = useAsyncCallback(async () => {
|
||||||
await apis?.popup?.dismissCurrentRecording();
|
await apis?.popup?.dismissCurrentRecording();
|
||||||
}, []);
|
track.popup.$.recordingBar.dismissRecording({
|
||||||
|
type: 'Meeting record',
|
||||||
|
appName: status?.appName || 'System Audio',
|
||||||
|
});
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
const handleStopRecording = useAsyncCallback(async () => {
|
const handleStopRecording = useAsyncCallback(async () => {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
track.popup.$.recordingBar.finishRecording({
|
||||||
|
type: 'Meeting record',
|
||||||
|
appName: status.appName || 'System Audio',
|
||||||
|
});
|
||||||
await apis?.recording?.stopRecording(status.id);
|
await apis?.recording?.stopRecording(status.id);
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
@@ -130,6 +139,12 @@ export function Recording() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// allow processing stopped event in tray menu as well:
|
// allow processing stopped event in tray menu as well:
|
||||||
return events?.recording.onRecordingStatusChanged(status => {
|
return events?.recording.onRecordingStatusChanged(status => {
|
||||||
|
if (status?.status === 'new') {
|
||||||
|
track.popup.$.recordingBar.toggleRecordingBar({
|
||||||
|
type: 'Meeting record',
|
||||||
|
appName: status.appName || 'System Audio',
|
||||||
|
});
|
||||||
|
}
|
||||||
if (status?.status === 'stopped') {
|
if (status?.status === 'stopped') {
|
||||||
handleProcessStoppedRecording();
|
handleProcessStoppedRecording();
|
||||||
}
|
}
|
||||||
@@ -140,6 +155,10 @@ export function Recording() {
|
|||||||
if (!status) {
|
if (!status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
track.popup.$.recordingBar.startRecording({
|
||||||
|
type: 'Meeting record',
|
||||||
|
appName: status.appName || 'System Audio',
|
||||||
|
});
|
||||||
await apis?.recording?.startRecording(status.appGroupId);
|
await apis?.recording?.startRecording(status.appGroupId);
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
{ "path": "../../electron-api" },
|
{ "path": "../../electron-api" },
|
||||||
{ "path": "../../i18n" },
|
{ "path": "../../i18n" },
|
||||||
{ "path": "../../../common/nbstore" },
|
{ "path": "../../../common/nbstore" },
|
||||||
|
{ "path": "../../track" },
|
||||||
{ "path": "../../../../blocksuite/affine/all" },
|
{ "path": "../../../../blocksuite/affine/all" },
|
||||||
{ "path": "../../../common/infra" },
|
{ "path": "../../../common/infra" },
|
||||||
{ "path": "../../../../tools/utils" }
|
{ "path": "../../../../tools/utils" }
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
|||||||
import type { AudioAttachmentBlock } from '@affine/core/modules/media/entities/audio-attachment-block';
|
import type { AudioAttachmentBlock } from '@affine/core/modules/media/entities/audio-attachment-block';
|
||||||
import { AudioAttachmentService } from '@affine/core/modules/media/services/audio-attachment';
|
import { AudioAttachmentService } from '@affine/core/modules/media/services/audio-attachment';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
|
import track from '@affine/track';
|
||||||
import type { AttachmentBlockModel } from '@blocksuite/affine/model';
|
import type { AttachmentBlockModel } from '@blocksuite/affine/model';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
@@ -71,6 +72,11 @@ const AttachmentAudioPlayer = ({ block }: { block: AudioAttachmentBlock }) => {
|
|||||||
|
|
||||||
if (transcribed) {
|
if (transcribed) {
|
||||||
block.expanded$.setValue(!expanded);
|
block.expanded$.setValue(!expanded);
|
||||||
|
track.doc.editor.audioBlock.openTranscribeNotes({
|
||||||
|
type: 'Meeting record',
|
||||||
|
method: 'success',
|
||||||
|
option: expanded ? 'off' : 'on',
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +93,10 @@ const AttachmentAudioPlayer = ({ block }: { block: AudioAttachmentBlock }) => {
|
|||||||
globalDialogService.open('sign-in', {});
|
globalDialogService.open('sign-in', {});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
track.doc.editor.audioBlock.openTranscribeNotes({
|
||||||
|
type: 'Meeting record',
|
||||||
|
method: 'not signed in',
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +118,17 @@ const AttachmentAudioPlayer = ({ block }: { block: AudioAttachmentBlock }) => {
|
|||||||
variant: 'primary',
|
variant: 'primary',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
track.doc.editor.audioBlock.openTranscribeNotes({
|
||||||
|
type: 'Meeting record',
|
||||||
|
method: 'not owner',
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await block.transcribe();
|
await block.transcribe();
|
||||||
|
track.doc.editor.audioBlock.transcribeRecording({
|
||||||
|
type: 'Meeting record',
|
||||||
|
method: 'success',
|
||||||
|
option: 'handle transcribing',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
enableAi,
|
enableAi,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hoo
|
|||||||
import { MeetingSettingsService } from '@affine/core/modules/media/services/meeting-settings';
|
import { MeetingSettingsService } from '@affine/core/modules/media/services/meeting-settings';
|
||||||
import type { MeetingSettingsSchema } from '@affine/electron/main/shared-state-schema';
|
import type { MeetingSettingsSchema } from '@affine/electron/main/shared-state-schema';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import track from '@affine/track';
|
||||||
import {
|
import {
|
||||||
ArrowRightSmallIcon,
|
ArrowRightSmallIcon,
|
||||||
DoneIcon,
|
DoneIcon,
|
||||||
@@ -106,6 +107,10 @@ export const MeetingsSettings = () => {
|
|||||||
const handleEnabledChange = useAsyncCallback(
|
const handleEnabledChange = useAsyncCallback(
|
||||||
async (checked: boolean) => {
|
async (checked: boolean) => {
|
||||||
try {
|
try {
|
||||||
|
track.$.settingsPanel.meetings.toggleMeetingFeatureFlag({
|
||||||
|
option: checked ? 'on' : 'off',
|
||||||
|
type: 'Meeting record',
|
||||||
|
});
|
||||||
await meetingSettingsService.setEnabled(checked);
|
await meetingSettingsService.setEnabled(checked);
|
||||||
} catch {
|
} catch {
|
||||||
confirmModal.openConfirmModal({
|
confirmModal.openConfirmModal({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { encodeAudioBlobToOpus } from '@affine/core/utils/webm-encoding';
|
import { encodeAudioBlobToOpus } from '@affine/core/utils/webm-encoding';
|
||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { AiJobStatus } from '@affine/graphql';
|
import { AiJobStatus } from '@affine/graphql';
|
||||||
|
import track from '@affine/track';
|
||||||
import {
|
import {
|
||||||
type AttachmentBlockModel,
|
type AttachmentBlockModel,
|
||||||
TranscriptionBlockFlavour,
|
TranscriptionBlockFlavour,
|
||||||
@@ -142,6 +143,10 @@ export class AudioAttachmentBlock extends Entity<AttachmentBlockModel> {
|
|||||||
this.fillTranscriptionResult(status.result);
|
this.fillTranscriptionResult(status.result);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
track.doc.editor.audioBlock.transcribeRecording({
|
||||||
|
type: 'Meeting record',
|
||||||
|
method: 'fail',
|
||||||
|
});
|
||||||
logger.error('Error transcribing audio:', error);
|
logger.error('Error transcribing audio:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"sideEffects": "false",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@sentry/react": "^9.2.0",
|
"@sentry/react": "^9.2.0",
|
||||||
|
|||||||
@@ -165,6 +165,18 @@ type IntegrationEvents =
|
|||||||
| 'completeIntegrationImport';
|
| 'completeIntegrationImport';
|
||||||
// END SECTION
|
// END SECTION
|
||||||
|
|
||||||
|
// SECTION: journal
|
||||||
|
type MeetingEvents =
|
||||||
|
| 'toggleRecordingBar'
|
||||||
|
| 'startRecording'
|
||||||
|
| 'dismissRecording'
|
||||||
|
| 'finishRecording'
|
||||||
|
| 'transcribeRecording'
|
||||||
|
| 'openTranscribeNotes'
|
||||||
|
| 'toggleMeetingFeatureFlag'
|
||||||
|
| 'activeMenubarAppItem';
|
||||||
|
// END SECTION
|
||||||
|
|
||||||
type UserEvents =
|
type UserEvents =
|
||||||
| GeneralEvents
|
| GeneralEvents
|
||||||
| AppEvents
|
| AppEvents
|
||||||
@@ -185,7 +197,8 @@ type UserEvents =
|
|||||||
| AttachmentEvents
|
| AttachmentEvents
|
||||||
| TemplateEvents
|
| TemplateEvents
|
||||||
| NotificationEvents
|
| NotificationEvents
|
||||||
| IntegrationEvents;
|
| IntegrationEvents
|
||||||
|
| MeetingEvents;
|
||||||
interface PageDivision {
|
interface PageDivision {
|
||||||
[page: string]: {
|
[page: string]: {
|
||||||
[segment: string]: {
|
[segment: string]: {
|
||||||
@@ -262,6 +275,7 @@ const PageEvents = {
|
|||||||
'abortIntegrationImport',
|
'abortIntegrationImport',
|
||||||
'completeIntegrationImport',
|
'completeIntegrationImport',
|
||||||
],
|
],
|
||||||
|
meetings: ['toggleMeetingFeatureFlag'],
|
||||||
},
|
},
|
||||||
cmdk: {
|
cmdk: {
|
||||||
recent: ['recentDocs'],
|
recent: ['recentDocs'],
|
||||||
@@ -403,6 +417,7 @@ const PageEvents = {
|
|||||||
],
|
],
|
||||||
aiActions: ['requestSignIn'],
|
aiActions: ['requestSignIn'],
|
||||||
starterBar: ['quickStart', 'openTemplateListMenu'],
|
starterBar: ['quickStart', 'openTemplateListMenu'],
|
||||||
|
audioBlock: ['transcribeRecording', 'openTranscribeNotes'],
|
||||||
},
|
},
|
||||||
inlineDocInfo: {
|
inlineDocInfo: {
|
||||||
$: ['toggle'],
|
$: ['toggle'],
|
||||||
@@ -459,6 +474,21 @@ const PageEvents = {
|
|||||||
$: ['checkout'],
|
$: ['checkout'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
menubarApp: {
|
||||||
|
menubarActionsMenu: {
|
||||||
|
menubarActionsList: ['activeMenubarAppItem', 'startRecording'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
popup: {
|
||||||
|
$: {
|
||||||
|
recordingBar: [
|
||||||
|
'toggleRecordingBar',
|
||||||
|
'startRecording',
|
||||||
|
'dismissRecording',
|
||||||
|
'finishRecording',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const satisfies PageDivision;
|
} as const satisfies PageDivision;
|
||||||
|
|
||||||
type OrganizeItemType = 'doc' | 'folder' | 'collection' | 'tag' | 'favorite';
|
type OrganizeItemType = 'doc' | 'folder' | 'collection' | 'tag' | 'favorite';
|
||||||
@@ -520,6 +550,12 @@ type IntegrationArgs<T extends Record<string, any>> = {
|
|||||||
control: 'Readwise Card' | 'Readwise settings' | 'Readwise import list';
|
control: 'Readwise Card' | 'Readwise settings' | 'Readwise import list';
|
||||||
} & T;
|
} & T;
|
||||||
|
|
||||||
|
type RecordingEventArgs = {
|
||||||
|
type: 'Meeting record';
|
||||||
|
method?: string;
|
||||||
|
option?: 'Auto transcribing' | 'handle transcribing' | 'on' | 'off';
|
||||||
|
};
|
||||||
|
|
||||||
export type EventArgs = {
|
export type EventArgs = {
|
||||||
createWorkspace: { flavour: string };
|
createWorkspace: { flavour: string };
|
||||||
signIn: AuthArgs;
|
signIn: AuthArgs;
|
||||||
@@ -622,6 +658,45 @@ export type EventArgs = {
|
|||||||
done: number;
|
done: number;
|
||||||
total: number;
|
total: number;
|
||||||
}>;
|
}>;
|
||||||
|
toggleRecordingBar: RecordingEventArgs & {
|
||||||
|
method: string;
|
||||||
|
appName: string;
|
||||||
|
};
|
||||||
|
startRecording: RecordingEventArgs & {
|
||||||
|
method: string;
|
||||||
|
appName: string;
|
||||||
|
};
|
||||||
|
dismissRecording: RecordingEventArgs & {
|
||||||
|
method: string;
|
||||||
|
appName: string;
|
||||||
|
};
|
||||||
|
finishRecording: RecordingEventArgs & {
|
||||||
|
method: 'fail' | 'success';
|
||||||
|
appName: string;
|
||||||
|
};
|
||||||
|
transcribeRecording: RecordingEventArgs & {
|
||||||
|
method: 'fail' | 'success';
|
||||||
|
option: 'Auto transcribing' | 'handle transcribing';
|
||||||
|
};
|
||||||
|
openTranscribeNotes: RecordingEventArgs & {
|
||||||
|
method: 'success' | 'reach limit' | 'not signed in' | 'not owner';
|
||||||
|
option: 'on' | 'off';
|
||||||
|
};
|
||||||
|
toggleMeetingFeatureFlag: RecordingEventArgs & {
|
||||||
|
option: 'on' | 'off';
|
||||||
|
};
|
||||||
|
activeMenubarAppItem: RecordingEventArgs & {
|
||||||
|
control:
|
||||||
|
| 'Open Journal'
|
||||||
|
| 'New Page'
|
||||||
|
| 'New Edgeless'
|
||||||
|
| 'Start recording meeting'
|
||||||
|
| 'Stop recording'
|
||||||
|
| 'Open AFFiNE'
|
||||||
|
| 'About AFFiNE'
|
||||||
|
| 'Meeting Settings'
|
||||||
|
| 'Quit AFFiNE Completely';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// for type checking
|
// for type checking
|
||||||
|
|||||||
@@ -949,6 +949,7 @@ export const PackageList = [
|
|||||||
'packages/frontend/electron-api',
|
'packages/frontend/electron-api',
|
||||||
'packages/frontend/i18n',
|
'packages/frontend/i18n',
|
||||||
'packages/common/nbstore',
|
'packages/common/nbstore',
|
||||||
|
'packages/frontend/track',
|
||||||
'blocksuite/affine/all',
|
'blocksuite/affine/all',
|
||||||
'packages/common/infra',
|
'packages/common/infra',
|
||||||
'tools/utils',
|
'tools/utils',
|
||||||
|
|||||||
@@ -509,6 +509,7 @@ __metadata:
|
|||||||
"@affine/electron-api": "workspace:*"
|
"@affine/electron-api": "workspace:*"
|
||||||
"@affine/i18n": "workspace:*"
|
"@affine/i18n": "workspace:*"
|
||||||
"@affine/nbstore": "workspace:*"
|
"@affine/nbstore": "workspace:*"
|
||||||
|
"@affine/track": "workspace:*"
|
||||||
"@blocksuite/affine": "workspace:*"
|
"@blocksuite/affine": "workspace:*"
|
||||||
"@emotion/react": "npm:^11.14.0"
|
"@emotion/react": "npm:^11.14.0"
|
||||||
"@sentry/react": "npm:^9.2.0"
|
"@sentry/react": "npm:^9.2.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user