mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
chore: add ga for most actions (#4829)
This commit is contained in:
parent
e940aeb1a3
commit
f242721f3b
40 changed files with 221 additions and 93 deletions
|
@ -24,6 +24,7 @@ import {
|
|||
import Stack from "./Stack";
|
||||
import { ToolButton } from "./ToolButton";
|
||||
import { hasStrokeColor } from "../scene/comparisons";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { hasBoundTextElement, isBoundToContainer } from "../element/typeChecks";
|
||||
|
||||
export const SelectedShapeActions = ({
|
||||
|
@ -209,6 +210,9 @@ export const ShapesSwitcher = ({
|
|||
activeToolType: typeof SHAPES[number]["value"];
|
||||
pointerType: PointerType | null;
|
||||
}) => {
|
||||
if (appState.activeTool.type !== activeToolType) {
|
||||
trackEvent("toolbar", activeToolType, "ui");
|
||||
}
|
||||
if (!appState.penDetected && pointerType === "pen") {
|
||||
setAppState({
|
||||
penDetected: true,
|
||||
|
|
|
@ -38,7 +38,6 @@ import { ActionResult } from "../actions/types";
|
|||
import { trackEvent } from "../analytics";
|
||||
import { getDefaultAppState, isEraserActive } from "../appState";
|
||||
import {
|
||||
copyToClipboard,
|
||||
parseClipboard,
|
||||
probablySupportsClipboardBlob,
|
||||
probablySupportsClipboardWriteText,
|
||||
|
@ -1291,12 +1290,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||
});
|
||||
|
||||
private cutAll = () => {
|
||||
this.copyAll();
|
||||
this.actionManager.executeAction(actionDeleteSelected);
|
||||
this.actionManager.executeAction(actionCut, "keyboard");
|
||||
};
|
||||
|
||||
private copyAll = () => {
|
||||
copyToClipboard(this.scene.getElements(), this.state, this.files);
|
||||
this.actionManager.executeAction(actionCopy, "keyboard");
|
||||
};
|
||||
|
||||
private static resetTapTwice() {
|
||||
|
@ -1570,7 +1568,14 @@ class App extends React.Component<AppProps, AppState> {
|
|||
gesture.pointers.delete(event.pointerId);
|
||||
};
|
||||
|
||||
toggleLock = () => {
|
||||
toggleLock = (source: "keyboard" | "ui" = "ui") => {
|
||||
if (!this.state.elementLocked) {
|
||||
trackEvent(
|
||||
"toolbar",
|
||||
"toggleLock",
|
||||
`${source} (${this.deviceType.isMobile ? "mobile" : "desktop"})`,
|
||||
);
|
||||
}
|
||||
this.setState((prevState) => {
|
||||
return {
|
||||
elementLocked: !prevState.elementLocked,
|
||||
|
@ -1594,9 +1599,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
};
|
||||
|
||||
toggleStats = () => {
|
||||
if (!this.state.showStats) {
|
||||
trackEvent("dialog", "stats");
|
||||
}
|
||||
this.actionManager.executeAction(actionToggleStats);
|
||||
};
|
||||
|
||||
|
@ -1851,9 +1853,16 @@ class App extends React.Component<AppProps, AppState> {
|
|||
) {
|
||||
const shape = findShapeByKey(event.key);
|
||||
if (shape) {
|
||||
if (this.state.activeTool.type !== shape) {
|
||||
trackEvent(
|
||||
"toolbar",
|
||||
shape,
|
||||
`keyboard (${this.deviceType.isMobile ? "mobile" : "desktop"})`,
|
||||
);
|
||||
}
|
||||
this.setActiveTool({ type: shape });
|
||||
} else if (event.key === KEYS.Q) {
|
||||
this.toggleLock();
|
||||
this.toggleLock("keyboard");
|
||||
}
|
||||
}
|
||||
if (event.key === KEYS.SPACE && gesture.pointers.size === 0) {
|
||||
|
@ -5493,6 +5502,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
options: [
|
||||
this.deviceType.isMobile &&
|
||||
navigator.clipboard && {
|
||||
trackEvent: false,
|
||||
name: "paste",
|
||||
perform: (elements, appStates) => {
|
||||
this.pasteFromClipboard(null);
|
||||
|
@ -5549,6 +5559,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.deviceType.isMobile &&
|
||||
navigator.clipboard && {
|
||||
name: "paste",
|
||||
trackEvent: false,
|
||||
perform: (elements, appStates) => {
|
||||
this.pasteFromClipboard(null);
|
||||
return {
|
||||
|
|
|
@ -70,7 +70,9 @@ const ContextMenu = ({
|
|||
dangerous: actionName === "deleteSelectedElements",
|
||||
checkmark: option.checked?.(appState),
|
||||
})}
|
||||
onClick={() => actionManager.executeAction(option)}
|
||||
onClick={() =>
|
||||
actionManager.executeAction(option, "contextMenu")
|
||||
}
|
||||
>
|
||||
<div className="context-menu-option__label">{label}</div>
|
||||
<kbd className="context-menu-option__shortcut">
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { render, unmountComponentAtNode } from "react-dom";
|
||||
import { ActionsManagerInterface } from "../actions/types";
|
||||
import { probablySupportsClipboardBlob } from "../clipboard";
|
||||
import { canvasToBlob } from "../data/blob";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
|
@ -19,6 +18,7 @@ import OpenColor from "open-color";
|
|||
import { CheckboxItem } from "./CheckboxItem";
|
||||
import { DEFAULT_EXPORT_PADDING } from "../constants";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import { ActionManager } from "../actions/manager";
|
||||
|
||||
const supportsContextFilters =
|
||||
"filter" in document.createElement("canvas").getContext("2d")!;
|
||||
|
@ -90,7 +90,7 @@ const ImageExportModal = ({
|
|||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
files: BinaryFiles;
|
||||
exportPadding?: number;
|
||||
actionManager: ActionsManagerInterface;
|
||||
actionManager: ActionManager;
|
||||
onExportToPng: ExportCB;
|
||||
onExportToSvg: ExportCB;
|
||||
onExportToClipboard: ExportCB;
|
||||
|
@ -229,7 +229,7 @@ export const ImageExportDialog = ({
|
|||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
files: BinaryFiles;
|
||||
exportPadding?: number;
|
||||
actionManager: ActionsManagerInterface;
|
||||
actionManager: ActionManager;
|
||||
onExportToPng: ExportCB;
|
||||
onExportToSvg: ExportCB;
|
||||
onExportToClipboard: ExportCB;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import { ActionsManagerInterface } from "../actions/types";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
import { useDeviceType } from "./App";
|
||||
|
@ -12,6 +11,9 @@ import { Card } from "./Card";
|
|||
|
||||
import "./ExportDialog.scss";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { ActionManager } from "../actions/manager";
|
||||
import { getFrame } from "../utils";
|
||||
|
||||
export type ExportCB = (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
|
@ -29,7 +31,7 @@ const JSONExportModal = ({
|
|||
appState: AppState;
|
||||
files: BinaryFiles;
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
actionManager: ActionsManagerInterface;
|
||||
actionManager: ActionManager;
|
||||
onCloseRequest: () => void;
|
||||
exportOpts: ExportOpts;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
|
@ -54,7 +56,7 @@ const JSONExportModal = ({
|
|||
aria-label={t("exportDialog.disk_button")}
|
||||
showAriaLabel={true}
|
||||
onClick={() => {
|
||||
actionManager.executeAction(actionSaveFileToDisk);
|
||||
actionManager.executeAction(actionSaveFileToDisk, "ui");
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
@ -70,9 +72,10 @@ const JSONExportModal = ({
|
|||
title={t("exportDialog.link_button")}
|
||||
aria-label={t("exportDialog.link_button")}
|
||||
showAriaLabel={true}
|
||||
onClick={() =>
|
||||
onExportToBackend(elements, appState, files, canvas)
|
||||
}
|
||||
onClick={() => {
|
||||
onExportToBackend(elements, appState, files, canvas);
|
||||
trackEvent("export", "link", `ui (${getFrame()})`);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
@ -94,7 +97,7 @@ export const JSONExportDialog = ({
|
|||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
appState: AppState;
|
||||
files: BinaryFiles;
|
||||
actionManager: ActionsManagerInterface;
|
||||
actionManager: ActionManager;
|
||||
exportOpts: ExportOpts;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
}) => {
|
||||
|
|
|
@ -36,6 +36,7 @@ import { LibraryMenu } from "./LibraryMenu";
|
|||
import "./LayerUI.scss";
|
||||
import "./Toolbar.scss";
|
||||
import { PenModeButton } from "./PenModeButton";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { useDeviceType } from "../components/App";
|
||||
|
||||
interface LayerUIProps {
|
||||
|
@ -122,6 +123,7 @@ const LayerUI = ({
|
|||
const createExporter =
|
||||
(type: ExportType): ExportCB =>
|
||||
async (exportedElements) => {
|
||||
trackEvent("export", type, "ui");
|
||||
const fileHandle = await exportCanvas(
|
||||
type,
|
||||
exportedElements,
|
||||
|
@ -326,7 +328,7 @@ const LayerUI = ({
|
|||
<LockButton
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
checked={appState.elementLocked}
|
||||
onChange={onLockToggle}
|
||||
onChange={() => onLockToggle()}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
<Island
|
||||
|
@ -531,7 +533,7 @@ const LayerUI = ({
|
|||
renderImageExportDialog={renderImageExportDialog}
|
||||
setAppState={setAppState}
|
||||
onCollabButtonClick={onCollabButtonClick}
|
||||
onLockToggle={onLockToggle}
|
||||
onLockToggle={() => onLockToggle()}
|
||||
onPenModeToggle={onPenModeToggle}
|
||||
canvas={canvas}
|
||||
isCollaborating={isCollaborating}
|
||||
|
|
|
@ -19,6 +19,7 @@ import LibraryMenuItems from "./LibraryMenuItems";
|
|||
import { EVENT } from "../constants";
|
||||
import { KEYS } from "../keys";
|
||||
import { arrayToMap } from "../utils";
|
||||
import { trackEvent } from "../analytics";
|
||||
|
||||
const useOnClickOutside = (
|
||||
ref: RefObject<HTMLElement>,
|
||||
|
@ -157,6 +158,7 @@ export const LibraryMenu = ({
|
|||
|
||||
const addToLibrary = useCallback(
|
||||
async (elements: LibraryItem["elements"]) => {
|
||||
trackEvent("element", "addToLibrary", "ui");
|
||||
if (elements.some((element) => element.type === "image")) {
|
||||
return setAppState({
|
||||
errorMessage: "Support for adding images to the library coming soon!",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue