1 import metrics from '@proton/metrics'
2 import { getBrowserForMetrics } from './getBrowserForMetrics'
3 import { sendTelemetryReport } from '@proton/shared/lib/helpers/metrics'
4 import type { Api } from '@proton/shared/lib/interfaces'
5 import type { TelemetryDocsEvents } from '@proton/shared/lib/api/telemetry'
6 import { TelemetryMeasurementGroups } from '@proton/shared/lib/api/telemetry'
7 import type { SuggestionSummaryType } from '@proton/docs-shared/lib/SuggestionType'
8 import { ConnectionCloseMetrics } from '../../Realtime/ConnectionCloseMetrics'
9 import type { ConnectionCloseReason } from '@proton/docs-proto'
11 const HEARTBEAT_INTERVAL = 60_000
13 type MetricSuggestionType = 'insertion' | 'replacement' | 'deletion' | 'formatting' | 'style' | 'other'
15 const SuggestionTypeToMetricSuggestionType: Record<SuggestionSummaryType, MetricSuggestionType> = {
18 'property-change': 'style',
21 'link-change': 'other',
22 'style-change': 'style',
23 replace: 'replacement',
25 'delete-link': 'other',
26 'image-change': 'other',
27 'insert-image': 'insertion',
28 'delete-image': 'deletion',
29 'indent-change': 'formatting',
30 'insert-table': 'insertion',
31 'delete-table': 'deletion',
32 'insert-table-row': 'insertion',
33 'duplicate-table-row': 'insertion',
34 'delete-table-row': 'deletion',
35 'insert-table-column': 'insertion',
36 'delete-table-column': 'deletion',
37 'duplicate-table-column': 'insertion',
38 'block-type-change': 'other',
39 'insert-divider': 'insertion',
40 'delete-divider': 'deletion',
41 'clear-formatting': 'style',
42 'align-change': 'formatting',
45 type SuggestionResolution = 'accepted' | 'rejected'
47 export class MetricService {
48 private heartbeatInterval: NodeJS.Timeout | null = null
50 constructor(private readonly api: Api) {}
55 this.heartbeatInterval = setInterval(() => {
57 }, HEARTBEAT_INTERVAL)
61 metrics.docs_open_documents_heartbeat_total.increment({
62 browser: getBrowserForMetrics(),
67 if (this.heartbeatInterval) {
68 clearInterval(this.heartbeatInterval)
72 reportSuggestionsTelemetry(event: TelemetryDocsEvents): void {
73 void sendTelemetryReport({
75 measurementGroup: TelemetryMeasurementGroups.docsSuggestions,
80 reportFullyBlockingErrorModal(): void {
81 metrics.docs_alert_modal_total.increment({})
84 reportSuggestionCreated(type?: SuggestionSummaryType): void {
85 const metricType = type ? SuggestionTypeToMetricSuggestionType[type] : 'other'
86 metrics.docs_suggestions_created_total.increment({ type: metricType })
89 reportSuggestionResolved(resolution: SuggestionResolution): void {
90 metrics.docs_suggestions_resolved_total.increment({ type: resolution })
93 reportRealtimeDisconnect(reason: ConnectionCloseReason): void {
94 let type = ConnectionCloseMetrics[reason.props.code]
96 /** There is no 'other' option, so we use 'timeout' as a catch-all */
100 metrics.docs_realtime_disconnect_error_total.increment({ type })