fix(chat): debounce post-send action menu
Docker Image / Build and Push Docker Image (push) Successful in 1m56s
Docker Image / Build and Push Docker Image (push) Successful in 1m56s
This commit is contained in:
@@ -124,6 +124,36 @@ describe("ChatInputBar", () => {
|
|||||||
expect(getButton("Open chat actions")).not.toBeNull();
|
expect(getButton("Open chat actions")).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("suppresses the Facebook WebView click that follows a pointer send for 300ms", () => {
|
||||||
|
const now = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||||
|
renderBar();
|
||||||
|
setTextareaValue(getTextarea(), "Hello Maya");
|
||||||
|
const sendButton = getButton("Send message");
|
||||||
|
const pointerDown = new Event("pointerdown", {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
});
|
||||||
|
Object.defineProperty(pointerDown, "pointerType", { value: "touch" });
|
||||||
|
|
||||||
|
act(() => sendButton.dispatchEvent(pointerDown));
|
||||||
|
|
||||||
|
expect(mocks.dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mocks.dispatch).toHaveBeenCalledWith({
|
||||||
|
type: "ChatSendMessage",
|
||||||
|
content: "Hello Maya",
|
||||||
|
});
|
||||||
|
expect(getTextarea().value).toBe("");
|
||||||
|
expect(getButton("Open chat actions")).toBe(sendButton);
|
||||||
|
|
||||||
|
now.mockReturnValue(1_299);
|
||||||
|
act(() => sendButton.click());
|
||||||
|
expect(container.querySelector('[aria-label="Chat actions"]')).toBeNull();
|
||||||
|
|
||||||
|
now.mockReturnValue(1_300);
|
||||||
|
act(() => sendButton.click());
|
||||||
|
expect(container.querySelector('[aria-label="Chat actions"]')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("closes the menu on outside interaction and when disabled", () => {
|
it("closes the menu on outside interaction and when disabled", () => {
|
||||||
renderBar();
|
renderBar();
|
||||||
act(() => getButton("Open chat actions").click());
|
act(() => getButton("Open chat actions").click());
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { ChatSendButton } from "./chat-send-button";
|
|||||||
import styles from "./chat-input-bar.module.css";
|
import styles from "./chat-input-bar.module.css";
|
||||||
|
|
||||||
const log = new Logger("AppChatComponentsChatInputBar");
|
const log = new Logger("AppChatComponentsChatInputBar");
|
||||||
const POINTER_SEND_CLICK_DEDUPE_MS = 500;
|
const POINTER_SEND_DEBOUNCE_MS = 300;
|
||||||
const CHAT_ACTION_MENU_ID = "chat-composer-action-menu";
|
const CHAT_ACTION_MENU_ID = "chat-composer-action-menu";
|
||||||
|
|
||||||
export interface ChatInputBarProps {
|
export interface ChatInputBarProps {
|
||||||
@@ -82,7 +82,7 @@ export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
|
|||||||
const handleSend = (source: "click" | "keyboard" | "pointerdown") => {
|
const handleSend = (source: "click" | "keyboard" | "pointerdown") => {
|
||||||
if (source === "click") {
|
if (source === "click") {
|
||||||
const elapsed = Date.now() - lastPointerSendAtRef.current;
|
const elapsed = Date.now() - lastPointerSendAtRef.current;
|
||||||
if (elapsed >= 0 && elapsed < POINTER_SEND_CLICK_DEDUPE_MS) {
|
if (elapsed >= 0 && elapsed < POINTER_SEND_DEBOUNCE_MS) {
|
||||||
log.debug("[chat-input-bar] suppress duplicate click", {
|
log.debug("[chat-input-bar] suppress duplicate click", {
|
||||||
elapsed,
|
elapsed,
|
||||||
contentLength: input.length,
|
contentLength: input.length,
|
||||||
@@ -115,6 +115,15 @@ export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
|
|||||||
|
|
||||||
const handleMenuToggle = () => {
|
const handleMenuToggle = () => {
|
||||||
if (disabled || hasContent) return;
|
if (disabled || hasContent) return;
|
||||||
|
const elapsed = Date.now() - lastPointerSendAtRef.current;
|
||||||
|
if (elapsed >= 0 && elapsed < POINTER_SEND_DEBOUNCE_MS) {
|
||||||
|
log.debug("[chat-input-bar] suppress post-send menu toggle", {
|
||||||
|
elapsed,
|
||||||
|
disabled,
|
||||||
|
isFocused,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!isActionMenuOpen) textareaRef.current?.blur();
|
if (!isActionMenuOpen) textareaRef.current?.blur();
|
||||||
setIsActionMenuOpen((open) => !open);
|
setIsActionMenuOpen((open) => !open);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user