diff --git a/apps/web/src/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions.tsx b/apps/web/src/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions.tsx
index 58a90441c..6816bf76a 100644
--- a/apps/web/src/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions.tsx
+++ b/apps/web/src/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions.tsx
@@ -1,6 +1,6 @@
import type { ListShortcutsResponse, Shortcut } from '@buster/server-shared/shortcuts';
import { useNavigate } from '@tanstack/react-router';
-import { useMemo } from 'react';
+import { useMemo, useRef } from 'react';
import { useDeleteShortcut, useGetShortcut } from '@/api/buster_rest/shortcuts/queryRequests';
import { ErrorCard } from '@/components/ui/error/ErrorCard';
import { Trash } from '@/components/ui/icons';
@@ -16,7 +16,6 @@ import type {
MentionInputSuggestionsProps,
MentionInputSuggestionsRef,
} from '@/components/ui/inputs/MentionInputSuggestions';
-import { CircleSpinnerLoader } from '@/components/ui/loaders';
import { ShortcutPopoverContent } from './ShortcutPopoverContent';
export const SHORTCUT_MENTION_TRIGGER = '/';
@@ -28,34 +27,42 @@ export const useCreateShortcutsMentionsSuggestions = (
const navigate = useNavigate();
const createShortcutForMention = useCreateShortcutForMention();
+ const currentItemsRef = useRef(shortcuts);
+
+ currentItemsRef.current = shortcuts;
+
return useMemo(
() =>
createMentionSuggestionExtension({
trigger: SHORTCUT_MENTION_TRIGGER,
- items: [
- ...shortcuts.map(createShortcutForMention),
- { type: 'separator' as const },
- {
- value: 'manageShortcuts',
- label: 'Manage shortcuts',
- icon: ,
- doNotAddPipeOnSelect: true,
- onSelect: () => {
- navigate({
- to: '/app/home/shortcuts',
- });
+ items: ({ defaultQueryMentionsFilter, query }) => {
+ const shortcuts = currentItemsRef.current;
+ const allItems = [
+ ...shortcuts.map(createShortcutForMention),
+ { type: 'separator' as const },
+ {
+ value: 'manageShortcuts',
+ label: 'Manage shortcuts',
+ icon: ,
+ doNotAddPipeOnSelect: true,
+ onSelect: () => {
+ navigate({
+ to: '/app/home/shortcuts',
+ });
+ },
},
- },
- {
- value: 'createShortcut',
- label: 'Create shortcut',
- icon: ,
- doNotAddPipeOnSelect: true,
- onSelect: () => {
- setOpenCreateShortcutModal(true);
+ {
+ value: 'createShortcut',
+ label: 'Create shortcut',
+ icon: ,
+ doNotAddPipeOnSelect: true,
+ onSelect: () => {
+ setOpenCreateShortcutModal(true);
+ },
},
- },
- ],
+ ];
+ return defaultQueryMentionsFilter(query, allItems);
+ },
popoverContent: ShortcutPopoverContent,
onChangeTransform: (v) => {
const foundShortcut = shortcuts.find((shortcut) => shortcut.name === v.label);
@@ -97,8 +104,8 @@ export const useCreateShortcutForMention = () => {
label: 'Delete',
icon: ,
value: 'delete',
- onClick: () => {
- deleteShortcut({ id: shortcut.id });
+ onClick: async () => {
+ await deleteShortcut({ id: shortcut.id });
},
},
]}
diff --git a/apps/web/src/components/ui/inputs/MentionInput/MentionInput.stories.tsx b/apps/web/src/components/ui/inputs/MentionInput/MentionInput.stories.tsx
index b3afb878f..23e834762 100644
--- a/apps/web/src/components/ui/inputs/MentionInput/MentionInput.stories.tsx
+++ b/apps/web/src/components/ui/inputs/MentionInput/MentionInput.stories.tsx
@@ -3,7 +3,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
import { useMemo, useRef, useState } from 'react';
import { fn } from 'storybook/test';
import { createMentionSuggestionExtension } from './createMentionSuggestionOption';
-import { defaultQueryMentionsFilter } from './defaultQueryMentionsFilter';
import { MentionInput } from './MentionInput';
import type { MentionInputRef, MentionInputTriggerItem } from './MentionInput.types';
@@ -329,6 +328,10 @@ export const DynamicItems: Story = {
{ value: 'Initial Item 2', label: 'Initial Item 2' },
]);
const mentionInputRef = useRef(null);
+ const currentItemsRef = useRef(dynamicItems);
+
+ // Always keep the ref updated with the latest items
+ currentItemsRef.current = dynamicItems;
const addRandomItem = () => {
const randomNames = [
@@ -384,11 +387,15 @@ export const DynamicItems: Story = {
setDynamicItems([]);
};
+ // Create a stable function that always uses the current items from the ref
const dynamicSuggestions = useMemo(
() =>
createMentionSuggestionExtension({
trigger: '!',
- items: dynamicItems,
+ items: ({ query, defaultQueryMentionsFilter }) => {
+ const latestItems = currentItemsRef.current;
+ return defaultQueryMentionsFilter(query, latestItems);
+ },
pillStyling: {
className: () => {
return 'bg-gradient-to-r from-purple-100 to-pink-100 border-purple-300 text-purple-700 hover:from-purple-200 hover:to-pink-200';
@@ -403,7 +410,7 @@ export const DynamicItems: Story = {
);
},
}),
- [dynamicItems]
+ [dynamicItems] // Empty dependency array since we're using the ref for fresh data
);
return (
diff --git a/apps/web/src/components/ui/inputs/MentionInput/createMentionSuggestionOption.tsx b/apps/web/src/components/ui/inputs/MentionInput/createMentionSuggestionOption.tsx
index 5f87a61aa..2a9bd0ebb 100644
--- a/apps/web/src/components/ui/inputs/MentionInput/createMentionSuggestionOption.tsx
+++ b/apps/web/src/components/ui/inputs/MentionInput/createMentionSuggestionOption.tsx
@@ -20,14 +20,24 @@ export const createMentionSuggestionExtension = ({
pillStyling,
}: {
trigger: string;
- items: MentionInputTriggerItem[] | ((props: { query: string }) => MentionInputTriggerItem[]); //if no function is provided we will use a literal string match
+ items:
+ | MentionInputTriggerItem[]
+ | ((props: {
+ query: string;
+ defaultQueryMentionsFilter: typeof defaultQueryMentionsFilter;
+ }) => MentionInputTriggerItem[]); //if no function is provided we will use a literal string match
popoverContent?: MentionPopoverContentCallback;
pillStyling?: MentionStylePillProps;
onChangeTransform?: MentionSuggestionExtension['onChangeTransform'];
}): MentionSuggestionExtension => ({
char: trigger,
items:
- typeof items === 'function' ? items : ({ query }) => defaultQueryMentionsFilter(query, items),
+ //beware of stale closures here. We should use a ref to get the latest items
+ typeof items === 'function'
+ ? (props) => {
+ return items({ ...props, defaultQueryMentionsFilter });
+ }
+ : ({ query }) => defaultQueryMentionsFilter(query, items),
render: () => {
let component: ReactRenderer>;