Merge pull request #444 from buster-so/big-nate/bus-1327-prevent-spam-on-button-click

Big nate/bus 1327 prevent spam on button click
This commit is contained in:
Nate Kelley 2025-07-08 14:33:50 -06:00 committed by GitHub
commit 356e870145
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 22 deletions

View File

@ -16,7 +16,7 @@ import { useAppLayoutContextSelector } from '../BusterAppLayout';
import { BusterRoutes } from '@/routes/busterRoutes';
export const useBusterNewChat = () => {
const { mutateAsync: startNewChat } = useStartNewChat();
const { mutateAsync: startNewChat, isPending: isSubmittingChat } = useStartNewChat();
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
const getChatMessageMemoized = useGetChatMessageMemoized();
const getChatMemoized = useGetChatMemoized();
@ -57,7 +57,6 @@ export const useBusterNewChat = () => {
message_id: messageId
});
initializeNewChat(res);
}
);
@ -149,7 +148,8 @@ export const useBusterNewChat = () => {
onFollowUpChat,
onStartChatFromFile,
onReplaceMessageInChat,
onStopChat
onStopChat,
isSubmittingChat
};
};

View File

@ -8,6 +8,13 @@ import { NewChatInput } from './NewChatInput';
import { NewChatWarning } from './NewChatWarning';
import { useNewChatWarning } from './useNewChatWarning';
enum TimeOfDay {
MORNING = 'morning',
AFTERNOON = 'afternoon',
EVENING = 'evening',
NIGHT = 'night'
}
export const HomePageController: React.FC<Record<string, never>> = () => {
const newChatWarningProps = useNewChatWarning();
const { showWarning } = newChatWarningProps;
@ -15,18 +22,35 @@ export const HomePageController: React.FC<Record<string, never>> = () => {
const user = useUserConfigContextSelector((state) => state.user);
const userName = user?.name;
const isMorning = useMemo(() => {
const timeOfDay = useMemo(() => {
const now = new Date();
const hours = now.getHours();
return hours < 12;
if (hours >= 5 && hours < 12) {
return TimeOfDay.MORNING;
} else if (hours >= 12 && hours < 17) {
return TimeOfDay.AFTERNOON;
} else if (hours >= 17 && hours < 21) {
return TimeOfDay.EVENING;
} else {
return TimeOfDay.NIGHT;
}
}, []);
const greeting = useMemo(() => {
if (isMorning) {
return `Good morning, ${userName}`;
switch (timeOfDay) {
case TimeOfDay.MORNING:
return `Good morning, ${userName}`;
case TimeOfDay.AFTERNOON:
return `Good afternoon, ${userName}`;
case TimeOfDay.EVENING:
return `Good evening, ${userName}`;
case TimeOfDay.NIGHT:
return `Good night, ${userName}`;
default:
return `Hello, ${userName}`;
}
return `Good afternoon, ${userName}`;
}, [userName]);
}, [timeOfDay, userName]);
return (
<div className="flex flex-col items-center justify-center p-4.5">

View File

@ -22,7 +22,7 @@ export const NewChatInput: React.FC<Record<string, never>> = () => {
}, [inputValue]);
const onSubmit = useMemoizedFn(async (value: string) => {
if (disabledSubmit) return;
if (disabledSubmit || loading) return;
try {
setLoading(true);
const trimmedValue = value.trim();
@ -32,12 +32,6 @@ export const NewChatInput: React.FC<Record<string, never>> = () => {
}
});
const onStop = useMemoizedFn(() => {
setLoading(false);
textAreaRef.current?.focus();
textAreaRef.current?.select();
});
const onChange = useMemoizedFn((e: ChangeEvent<HTMLTextAreaElement>) => {
setInputValue(e.target.value);
});
@ -56,7 +50,6 @@ export const NewChatInput: React.FC<Record<string, never>> = () => {
autoResize={autoResizeConfig}
onSubmit={onSubmit}
onChange={onChange}
onStop={onStop}
loading={loading}
disabled={false}
disabledSubmit={disabledSubmit}

View File

@ -11,8 +11,9 @@ const autoResizeConfig = { minRows: 2, maxRows: 16 };
export const ChatInput: React.FC = React.memo(() => {
const textAreaRef = useRef<HTMLTextAreaElement>(null);
const loading = useChatIndividualContextSelector((x) => x.isStreamingMessage);
const isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage);
const hasChat = useChatIndividualContextSelector((x) => x.hasChat);
const [inputValue, setInputValue] = useState('');
const disableSubmit = useMemo(() => {
@ -23,7 +24,7 @@ export const ChatInput: React.FC = React.memo(() => {
disableSubmit,
inputValue,
setInputValue,
loading,
loading: isStreamingMessage,
textAreaRef
});
@ -42,7 +43,7 @@ export const ChatInput: React.FC = React.memo(() => {
onSubmit={onSubmitPreflight}
onChange={onChange}
onStop={onStopChat}
loading={loading}
loading={isStreamingMessage}
value={inputValue}
disabled={!hasChat}
disabledSubmit={disableSubmit}

View File

@ -1,5 +1,5 @@
import type React from 'react';
import { useMemo } from 'react';
import { useMemo, useRef } from 'react';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { useBusterNewChatContextSelector } from '@/context/Chats';
import { useMemoizedFn } from '@/hooks';
@ -27,6 +27,7 @@ export const useChatInputFlow = ({
const selectedFileId = useChatIndividualContextSelector((x) => x.selectedFileId);
const onStartNewChat = useBusterNewChatContextSelector((state) => state.onStartNewChat);
const onFollowUpChat = useBusterNewChatContextSelector((state) => state.onFollowUpChat);
const isSubmittingChat = useBusterNewChatContextSelector((state) => state.isSubmittingChat);
const onStartChatFromFile = useBusterNewChatContextSelector((state) => state.onStartChatFromFile);
const onStopChatContext = useBusterNewChatContextSelector((state) => state.onStopChat);
const currentMessageId = useChatIndividualContextSelector((x) => x.currentMessageId);
@ -34,6 +35,8 @@ export const useChatInputFlow = ({
const onResetToOriginal = useChatIndividualContextSelector((x) => x.onResetToOriginal);
const { openConfirmModal } = useBusterNotifications();
const submittingCooldown = useRef(isSubmittingChat);
const flow: FlowType = useMemo(() => {
if (hasChat) return 'followup-chat';
if (selectedFileType === 'metric' && selectedFileId) return 'followup-metric';
@ -42,7 +45,14 @@ export const useChatInputFlow = ({
}, [hasChat, selectedFileType, selectedFileId]);
const onSubmitPreflight = useMemoizedFn(async () => {
if (disableSubmit || !chatId || !currentMessageId) return;
if (
disableSubmit ||
!chatId ||
!currentMessageId ||
submittingCooldown.current ||
isSubmittingChat
)
return;
if (loading) {
onStopChat();
@ -52,6 +62,7 @@ export const useChatInputFlow = ({
const trimmedInputValue = inputValue.trim();
const method = async () => {
submittingCooldown.current = true;
switch (flow) {
case 'followup-chat':
await onFollowUpChat({ prompt: trimmedInputValue, chatId });
@ -89,6 +100,10 @@ export const useChatInputFlow = ({
setTimeout(() => {
textAreaRef.current?.focus();
}, 50);
setTimeout(() => {
submittingCooldown.current = false;
}, 350);
};
if (!isFileChanged) {