UNPKG

@atlaskit/editor-plugin-interactivity

Version:

Interactivity plugin for @atlaskit/editor-core

91 lines (89 loc) 5.3 kB
import { useEffect, useRef } from 'react'; import { logException } from '@atlaskit/editor-common/monitoring'; import { fireInteractivityEvent } from './analytics/fire-interactivity-event'; import { InteractivityCollector } from './collector/interactivity-collector'; /** * Reports session-to-date interaction latency distributions for full page editor sessions * as the `editor interactivity` operational event. * * The session runs for as long as the plugin's hook stays mounted, which is one editor * mount: `MountPluginHooks` in editor-core keys hook fibers by plugin name, so a preset * reconfigure — which destroys and recreates ProseMirror plugin views — leaves this hook, * and the session, in place. */ export const interactivityPlugin = ({ api }) => ({ name: 'interactivity', usePluginHook({ editorView, wrapperElement }) { const collectorRef = useRef(undefined); useEffect(() => { try { var _api$contextIdentifie4, _api$editorViewMode2; const collector = new InteractivityCollector({ emit: snapshot => { var _api$analytics; return fireInteractivityEvent(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions, snapshot); }, getObjectId: () => { var _api$contextIdentifie, _api$contextIdentifie2, _api$contextIdentifie3; return api === null || api === void 0 ? void 0 : (_api$contextIdentifie = api.contextIdentifier) === null || _api$contextIdentifie === void 0 ? void 0 : (_api$contextIdentifie2 = _api$contextIdentifie.sharedState.currentState()) === null || _api$contextIdentifie2 === void 0 ? void 0 : (_api$contextIdentifie3 = _api$contextIdentifie2.contextIdentifierProvider) === null || _api$contextIdentifie3 === void 0 ? void 0 : _api$contextIdentifie3.objectId; }, getSessionMode: () => { var _api$editorViewMode, _api$editorViewMode$s; const mode = api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode; if (mode === undefined) { return undefined; } return mode === 'view' ? 'reading' : 'editing'; }, // A destroyed view still answers, with the document it was destroyed with, so // both report nothing rather than a size the editor no longer has. getNodeSize: () => editorView.isDestroyed ? undefined : editorView.state.doc.nodeSize, // `getElementsByTagName` counts descendants in the browser engine, so this // cannot overflow the stack on very large documents. getEditorDomSize: () => editorView.isDestroyed ? undefined : editorView.dom.getElementsByTagName('*').length }); // Effects run in declaration order, so the one below always finds it. collectorRef.current = collector; if (!collector.start()) { return; } // Confluence live pages navigate and switch between reading and editing without // remounting the editor. A session covers one document in one mode, so either // change ends it and starts the next. const unsubscribeFromObjectId = api === null || api === void 0 ? void 0 : (_api$contextIdentifie4 = api.contextIdentifier) === null || _api$contextIdentifie4 === void 0 ? void 0 : _api$contextIdentifie4.sharedState.onChange(() => collector.onObjectIdChanged()); const unsubscribeFromViewMode = api === null || api === void 0 ? void 0 : (_api$editorViewMode2 = api.editorViewMode) === null || _api$editorViewMode2 === void 0 ? void 0 : _api$editorViewMode2.sharedState.onChange(() => collector.onViewModeChanged()); return () => { unsubscribeFromObjectId === null || unsubscribeFromObjectId === void 0 ? void 0 : unsubscribeFromObjectId(); unsubscribeFromViewMode === null || unsubscribeFromViewMode === void 0 ? void 0 : unsubscribeFromViewMode(); collector.stop(); }; } catch (error) { // Instrumentation must not fail the editor around it, but a failure that hits every // browser of one engine would be invisible without this. void logException(error, { location: 'editor-plugin-interactivity/start' }); return; } // `api` is deliberately not a dependency: a preset reconfigure hands out a new // proxy object, but the one captured here keeps resolving plugins from the same // live registry, and restarting the session on a reconfigure would split one // editor session in two. }, [editorView]); useEffect(() => { try { var _collectorRef$current; (_collectorRef$current = collectorRef.current) === null || _collectorRef$current === void 0 ? void 0 : _collectorRef$current.setEditorRoot(wrapperElement); } catch (error) { void logException(error, { location: 'editor-plugin-interactivity/observe' }); } }, [wrapperElement]); } });