Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {useMemo} from 'react';

import {useHotkeys} from 'sentry/utils/useHotkeys';

import type {Shortcut} from './types';
Expand All @@ -11,15 +9,13 @@ function SearchHotkeysListener({
runShortcut: (shortcut: Shortcut) => void;
visibleShortcuts: Shortcut[];
}) {
const hotkeys = useMemo(() => {
return visibleShortcuts
.filter(shortcut => typeof shortcut.hotkeys !== 'undefined')
.map(shortcut => ({
match: shortcut.hotkeys?.actual ?? [],
includeInputs: true,
callback: () => runShortcut(shortcut),
}));
}, [visibleShortcuts, runShortcut]);
const hotkeys = visibleShortcuts
.filter(shortcut => typeof shortcut.hotkeys !== 'undefined')
.map(shortcut => ({
match: shortcut.hotkeys?.actual ?? [],
includeInputs: true,
callback: () => runShortcut(shortcut),
}));

useHotkeys(hotkeys);
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {ComponentProps} from 'react';
import {Fragment, useCallback, useMemo, useState} from 'react';
import {Fragment, useState} from 'react';
import {css} from '@emotion/react';
import styled from '@emotion/styled';

Expand Down Expand Up @@ -65,25 +65,19 @@ export default function ScreenshotModal({
const currentAttachmentIndex = screenshots.findIndex(
attachment => attachment.id === currentEventAttachment.id
);
const paginateItems = useCallback(
(delta: number) => {
if (screenshots.length) {
const newIndex = currentAttachmentIndex + delta;
if (newIndex >= 0 && newIndex < screenshots.length) {
setCurrentAttachment(screenshots[newIndex]!);
}
const paginateItems = (delta: number) => {
if (screenshots.length) {
const newIndex = currentAttachmentIndex + delta;
if (newIndex >= 0 && newIndex < screenshots.length) {
setCurrentAttachment(screenshots[newIndex]!);
}
},
[screenshots, currentAttachmentIndex]
);
}
};

const paginateHotkeys = useMemo(() => {
return [
{match: 'right', callback: () => paginateItems(1)},
{match: 'left', callback: () => paginateItems(-1)},
];
}, [paginateItems]);
useHotkeys(paginateHotkeys);
useHotkeys([
{match: 'right', callback: () => paginateItems(1)},
{match: 'left', callback: () => paginateItems(-1)},
]);

const {dateCreated, size, mimetype} = currentEventAttachment;

Expand Down
23 changes: 9 additions & 14 deletions static/app/components/globalDrawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
useContext,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
Expand Down Expand Up @@ -179,20 +178,16 @@ export function GlobalDrawer({children}: any) {
});

// Close the drawer when escape is pressed and options allow it.
const globalDrawerHotkeys = useMemo(() => {
return [
{
match: 'Escape',
callback: () => {
if (currentDrawerConfig?.options?.closeOnEscapeKeypress ?? true) {
handleClose();
}
},
useHotkeys([
{
match: 'Escape',
callback: () => {
if (currentDrawerConfig?.options?.closeOnEscapeKeypress ?? true) {
handleClose();
}
},
];
}, [currentDrawerConfig?.options?.closeOnEscapeKeypress, handleClose]);

useHotkeys(globalDrawerHotkeys);
},
]);

const renderedChild = currentDrawerConfig?.renderer
? currentDrawerConfig.renderer({
Expand Down
52 changes: 21 additions & 31 deletions static/app/components/tours/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,37 +107,27 @@ export function TourContextProvider<T extends TourEnumType>({
const {endTour, previousStep, nextStep, currentStepId} = tourContextValue;
const isTourActive = currentStepId !== null;

const tourHotkeys = useMemo(() => {
if (!isTourActive) {
return [];
}

return [
{
match: 'Escape',
callback: () => {
if (tourKey) {
mutate({guide: tourKey, status: 'dismissed'});
}
trackAnalytics('tour-guide.dismiss', {organization, id: `${currentStepId}`});
endTour();
},
},
{match: ['left', 'h'], callback: () => previousStep()},
{match: ['right', 'l'], callback: () => nextStep()},
];
}, [
isTourActive,
tourKey,
organization,
currentStepId,
endTour,
mutate,
previousStep,
nextStep,
]);

useHotkeys(tourHotkeys);
useHotkeys(
isTourActive
? [
{
match: 'Escape',
callback: () => {
if (tourKey) {
mutate({guide: tourKey, status: 'dismissed'});
}
trackAnalytics('tour-guide.dismiss', {
organization,
id: `${currentStepId}`,
});
endTour();
},
},
{match: ['left', 'h'], callback: () => previousStep()},
{match: ['right', 'l'], callback: () => nextStep()},
]
: []
);

useEffect(() => {
if (isTourActive && tourKey) {
Expand Down
6 changes: 1 addition & 5 deletions static/app/stories/view/storySearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ export function StorySearch() {
const core = useMemo(() => coreTree.flatMap(tree => tree.flat()), [coreTree]);
const shared = useMemo(() => sharedTree.flatMap(tree => tree.flat()), [sharedTree]);

const storiesSearchHotkeys = useMemo(() => {
return [{match: '/', callback: () => inputRef.current?.focus()}];
}, []);

useHotkeys(storiesSearchHotkeys);
useHotkeys([{match: '/', callback: () => inputRef.current?.focus()}]);

const sectionedItems = useMemo(() => {
const sections: StorySection[] = [];
Expand Down
10 changes: 4 additions & 6 deletions static/app/utils/theme/useThemeSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,10 @@ export function useThemeSwitcher(): DO_NOT_USE_ChonkTheme | Theme {
[user?.options?.prefersChonkUI, config.theme, mutateUserOptions]
);

const themeToggleHotkeys = useMemo(() => {
return organization?.features?.includes('chonk-ui')
useHotkeys(
organization?.features?.includes('chonk-ui')
? [themeToggleHotkey, chonkThemeToggleHotkey]
: [themeToggleHotkey];
}, [organization, chonkThemeToggleHotkey, themeToggleHotkey]);

useHotkeys(themeToggleHotkeys);
: [themeToggleHotkey]
);
return theme;
}
21 changes: 12 additions & 9 deletions static/app/utils/useHotkeys.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useCallback, useEffect} from 'react';
import {useEffect, useRef} from 'react';

import toArray from 'sentry/utils/array/toArray';

Expand Down Expand Up @@ -56,9 +56,15 @@ type Hotkey = {
* Note: you can only use one non-modifier (keys other than shift, ctrl, alt, command) key at a time.
*/
export function useHotkeys(hotkeys: Hotkey[]): void {
const onKeyDown = useCallback(
(evt: KeyboardEvent) => {
for (const hotkey of hotkeys) {
const hotkeysRef = useRef(hotkeys);

useEffect(() => {
hotkeysRef.current = hotkeys;
});

useEffect(() => {
const onKeyDown = (evt: KeyboardEvent) => {
for (const hotkey of hotkeysRef.current) {
const preventDefault = !hotkey.skipPreventDefault;
const keysets = toArray(hotkey.match).map(keys => keys.toLowerCase());

Expand All @@ -84,15 +90,12 @@ export function useHotkeys(hotkeys: Hotkey[]): void {
}
}
}
},
[hotkeys]
);
};

useEffect(() => {
document.addEventListener('keydown', onKeyDown);

return () => {
document.removeEventListener('keydown', onKeyDown);
};
}, [onKeyDown]);
}, []);
}
50 changes: 22 additions & 28 deletions static/app/views/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {lazy, Suspense, useCallback, useEffect, useMemo, useRef} from 'react';
import {lazy, Suspense, useCallback, useEffect, useRef} from 'react';
import styled from '@emotion/styled';

import {
Expand Down Expand Up @@ -62,37 +62,31 @@ function App({children, params}: Props) {
const preloadData = shouldPreloadData(config);

// Command palette global-shortcut
const commandPaletteHotkeys = useMemo(() => {
if (isModalOpen) {
return [];
}
return [
{
match: ['command+shift+p', 'command+k', 'ctrl+shift+p', 'ctrl+k'],
callback: () => openCommandPalette(),
},
];
}, [isModalOpen]);

useHotkeys(commandPaletteHotkeys);
useHotkeys(
isModalOpen
? []
: [
{
match: ['command+shift+p', 'command+k', 'ctrl+shift+p', 'ctrl+k'],
callback: () => openCommandPalette(),
},
]
);

// Seer explorer panel hook and hotkeys
const {isOpen: isExplorerPanelOpen, toggleExplorerPanel} = useExplorerPanel();

const explorerPanelHotkeys = useMemo(() => {
if (isModalOpen) {
return [];
}
return [
{
match: ['command+/', 'ctrl+/'],
callback: () => toggleExplorerPanel(),
includeInputs: true,
},
];
}, [isModalOpen, toggleExplorerPanel]);

useHotkeys(explorerPanelHotkeys);
useHotkeys(
isModalOpen
? []
: [
{
match: ['command+/', 'ctrl+/'],
callback: () => toggleExplorerPanel(),
includeInputs: true,
},
]
);

/**
* Loads the users organization list into the OrganizationsStore
Expand Down
8 changes: 2 additions & 6 deletions static/app/views/settings/components/settingsSearch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useMemo, useRef} from 'react';
import {useRef} from 'react';
import styled from '@emotion/styled';

import {InputGroup} from 'sentry/components/core/input/inputGroup';
Expand All @@ -13,11 +13,7 @@ const MAX_RESULTS = 10;
function SettingsSearch() {
const searchInput = useRef<HTMLInputElement>(null);

const settingsSearchHotkeys = useMemo(() => {
return [{match: '/', callback: () => searchInput.current?.focus()}];
}, []);

useHotkeys(settingsSearchHotkeys);
useHotkeys([{match: '/', callback: () => searchInput.current?.focus()}]);

return (
<Search
Expand Down
Loading