From 014848b28a4814d4f9721631ecefe04d931f8bd6 Mon Sep 17 00:00:00 2001 From: David Luzar <5153846+dwelle@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:39:19 +0200 Subject: [PATCH] fix: command palette tweaks and fixes (#7876) --- excalidraw-app/App.tsx | 15 ++++++++ excalidraw-app/components/AppMainMenu.tsx | 21 ++++++++-- .../components/AppWelcomeScreen.tsx | 4 +- excalidraw-app/index.scss | 2 +- .../__snapshots__/MobileMenu.test.tsx.snap | 25 ++++-------- packages/excalidraw/analytics.ts | 2 +- .../CommandPalette/CommandPalette.tsx | 38 ++++++++++++++----- packages/excalidraw/components/HelpDialog.tsx | 17 +++++++-- packages/excalidraw/components/icons.tsx | 21 ++++++++++ .../components/main-menu/DefaultItems.tsx | 9 ++++- 10 files changed, 115 insertions(+), 39 deletions(-) diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index 67b774f8e0ea..28331725c53d 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -121,6 +121,7 @@ import { usersIcon, exportToPlus, share, + youtubeIcon, } from "../packages/excalidraw/components/icons"; import { appThemeAtom, useHandleAppTheme } from "./useHandleAppTheme"; @@ -1140,6 +1141,20 @@ const ExcalidrawWrapper = () => { ); }, }, + { + label: "YouTube", + icon: youtubeIcon, + category: DEFAULT_CATEGORIES.links, + predicate: true, + keywords: ["features", "tutorials", "howto", "help", "community"], + perform: () => { + window.open( + "https://youtube.com/@excalidraw", + "_blank", + "noopener noreferrer", + ); + }, + }, ...(isExcalidrawPlusSignedUser ? [ { diff --git a/excalidraw-app/components/AppMainMenu.tsx b/excalidraw-app/components/AppMainMenu.tsx index 17c7bb3c5ae2..2cc055b323a8 100644 --- a/excalidraw-app/components/AppMainMenu.tsx +++ b/excalidraw-app/components/AppMainMenu.tsx @@ -1,6 +1,11 @@ import React from "react"; +import { + arrowBarToLeftIcon, + ExcalLogo, +} from "../../packages/excalidraw/components/icons"; import { Theme } from "../../packages/excalidraw/element/types"; import { MainMenu } from "../../packages/excalidraw/index"; +import { isExcalidrawPlusSignedUser } from "../app_constants"; import { LanguageList } from "./LanguageList"; export const AppMainMenu: React.FC<{ @@ -22,19 +27,29 @@ export const AppMainMenu: React.FC<{ onSelect={() => props.onCollabDialogOpen()} /> )} - + Excalidraw+ + + {isExcalidrawPlusSignedUser ? "Sign in" : "Sign up"} + - Try Excalidraw Plus! + Sign up )} diff --git a/excalidraw-app/index.scss b/excalidraw-app/index.scss index 24741b062bc7..d5cc4770cf30 100644 --- a/excalidraw-app/index.scss +++ b/excalidraw-app/index.scss @@ -38,7 +38,7 @@ background-color: #ecfdf5; color: #064e3c; } - &.ExcalidrawPlus { + &.highlighted { color: var(--color-promo); } } diff --git a/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap b/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap index ad0c9f0f1d34..b657dbb54513 100644 --- a/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap +++ b/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap @@ -216,32 +216,23 @@ exports[`Test MobileMenu > should initialize with welcome screen and hide once u stroke-width="2" viewBox="0 0 24 24" > - + - - @@ -249,7 +240,7 @@ exports[`Test MobileMenu > should initialize with welcome screen and hide once u
- Try Excalidraw Plus! + Sign up
diff --git a/packages/excalidraw/analytics.ts b/packages/excalidraw/analytics.ts index 671f59202d1b..bd4b6191ef98 100644 --- a/packages/excalidraw/analytics.ts +++ b/packages/excalidraw/analytics.ts @@ -1,6 +1,6 @@ // place here categories that you want to track. We want to track just a // small subset of categories at a given time. -const ALLOWED_CATEGORIES_TO_TRACK = ["ai"] as string[]; +const ALLOWED_CATEGORIES_TO_TRACK = ["ai", "command_palette"] as string[]; export const trackEvent = ( category: string, diff --git a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx index d7ed691c5625..f021632b8326 100644 --- a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx +++ b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx @@ -49,6 +49,8 @@ import { jotaiStore } from "../../jotai"; import { activeConfirmDialogAtom } from "../ActiveConfirmDialog"; import { CommandPaletteItem } from "./types"; import * as defaultItems from "./defaultCommandPaletteItems"; +import { trackEvent } from "../../analytics"; +import { useStable } from "../../hooks/useStable"; import "./CommandPalette.scss"; @@ -130,12 +132,20 @@ export const CommandPalette = Object.assign( if (isCommandPaletteToggleShortcut(event)) { event.preventDefault(); event.stopPropagation(); - setAppState((appState) => ({ - openDialog: + setAppState((appState) => { + const nextState = appState.openDialog?.name === "commandPalette" ? null - : { name: "commandPalette" }, - })); + : ({ name: "commandPalette" } as const); + + if (nextState) { + trackEvent("command_palette", "open", "shortcut"); + } + + return { + openDialog: nextState, + }; + }); } }; window.addEventListener(EVENT.KEYDOWN, commandPaletteShortcut, { @@ -174,10 +184,20 @@ function CommandPaletteInner({ const inputRef = useRef(null); + const stableDeps = useStable({ + uiAppState, + customCommandPaletteItems, + appProps, + }); + useEffect(() => { - if (!uiAppState || !app.scene || !actionManager) { - return; - } + // these props change often and we don't want them to re-run the effect + // which would renew `allCommands`, cascading down and resetting state. + // + // This means that the commands won't update on appState/appProps changes + // while the command palette is open + const { uiAppState, customCommandPaletteItems, appProps } = stableDeps; + const getActionLabel = (action: Action) => { let label = ""; if (action.label) { @@ -533,15 +553,13 @@ function CommandPaletteInner({ ); } }, [ + stableDeps, app, - appProps, - uiAppState, actionManager, setAllCommands, lastUsed?.label, setLastUsed, setAppState, - customCommandPaletteItems, ]); const [commandSearch, setCommandSearch] = useState(""); diff --git a/packages/excalidraw/components/HelpDialog.tsx b/packages/excalidraw/components/HelpDialog.tsx index 6f539963b188..23c9f8f47d3f 100644 --- a/packages/excalidraw/components/HelpDialog.tsx +++ b/packages/excalidraw/components/HelpDialog.tsx @@ -4,7 +4,7 @@ import { KEYS } from "../keys"; import { Dialog } from "./Dialog"; import { getShortcutKey } from "../utils"; import "./HelpDialog.scss"; -import { ExternalLinkIcon } from "./icons"; +import { ExternalLinkIcon, GithubIcon, youtubeIcon } from "./icons"; import { probablySupportsClipboardBlob } from "../clipboard"; import { isDarwin, isFirefox, isWindows } from "../constants"; import { getShortcutFromShortcutName } from "../actions/shortcuts"; @@ -17,8 +17,8 @@ const Header = () => ( target="_blank" rel="noopener noreferrer" > - {t("helpDialog.documentation")}
{ExternalLinkIcon}
+ {t("helpDialog.documentation")} ( target="_blank" rel="noopener noreferrer" > - {t("helpDialog.blog")}
{ExternalLinkIcon}
+ {t("helpDialog.blog")}
( target="_blank" rel="noopener noreferrer" > +
{GithubIcon}
{t("helpDialog.github")} -
{ExternalLinkIcon}
+
+ +
{youtubeIcon}
+ YouTube
); diff --git a/packages/excalidraw/components/icons.tsx b/packages/excalidraw/components/icons.tsx index cd76059c3785..e2e0d68e49b1 100644 --- a/packages/excalidraw/components/icons.tsx +++ b/packages/excalidraw/components/icons.tsx @@ -2094,3 +2094,24 @@ export const DeviceDesktopIcon = createIcon(
, { ...tablerIconProps, strokeWidth: 1.5 }, ); + +// arrow-bar-to-left +export const arrowBarToLeftIcon = createIcon( + + + + + + + , + tablerIconProps, +); + +export const youtubeIcon = createIcon( + + + + + , + tablerIconProps, +); diff --git a/packages/excalidraw/components/main-menu/DefaultItems.tsx b/packages/excalidraw/components/main-menu/DefaultItems.tsx index caacc05782ff..26ef260006e3 100644 --- a/packages/excalidraw/components/main-menu/DefaultItems.tsx +++ b/packages/excalidraw/components/main-menu/DefaultItems.tsx @@ -39,6 +39,7 @@ import Trans from "../Trans"; import DropdownMenuItemContentRadio from "../dropdownMenu/DropdownMenuItemContentRadio"; import { THEME } from "../../constants"; import type { Theme } from "../../element/types"; +import { trackEvent } from "../../analytics"; import "./DefaultItems.scss"; @@ -122,7 +123,7 @@ export const SaveAsImage = () => { }; SaveAsImage.displayName = "SaveAsImage"; -export const CommandPalette = () => { +export const CommandPalette = (opts?: { className?: string }) => { const setAppState = useExcalidrawSetAppState(); const { t } = useI18n(); @@ -130,9 +131,13 @@ export const CommandPalette = () => { setAppState({ openDialog: { name: "commandPalette" } })} + onSelect={() => { + trackEvent("command_palette", "open", "menu"); + setAppState({ openDialog: { name: "commandPalette" } }); + }} shortcut={getShortcutFromShortcutName("commandPalette")} aria-label={t("commandPalette.title")} + className={opts?.className} > {t("commandPalette.title")}