refactor(keyboard): remove input restore fallback logic
This commit is contained in:
@@ -28,11 +28,7 @@ export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
|
||||
|
||||
useKeyboardHeight({
|
||||
targetRef: textareaRef,
|
||||
containerRef: barRef,
|
||||
active: isFocused,
|
||||
onKeyboardDismiss: () => {
|
||||
setIsFocused(false);
|
||||
},
|
||||
});
|
||||
|
||||
const handleInputChange = (value: string) => {
|
||||
|
||||
@@ -4,6 +4,5 @@ export const FOCUS_GAP_PX = 14;
|
||||
export const FALLBACK_INPUT_LIFT_PX = 320;
|
||||
export const KEYBOARD_VISIBLE_THRESHOLD_PX = 80;
|
||||
export const KEYBOARD_VISIBLE_RATIO = 0.82;
|
||||
export const KEYBOARD_RELEASE_GRACE_MS = 900;
|
||||
export const KEYBOARD_MEASURE_DELAYS_MS = [0, 100, 250, 500];
|
||||
export const KEYBOARD_POLL_INTERVAL_MS = 300;
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
* - 非目标 Android Facebook IAB 设备:完全不生效。
|
||||
* - focus 后短期多次测量输入框是否被 visibleViewport 遮挡。
|
||||
* - 若 viewport 没有可靠变化,则使用固定 fallback 抬升。
|
||||
* - 聚焦期间轻量轮询;一旦检测到 viewport 恢复,主动 blur 并清除抬升。
|
||||
* - 聚焦期间轻量轮询,持续保持输入框避让。
|
||||
*/
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { Logger } from "@/utils";
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
FOCUS_GAP_PX,
|
||||
KEYBOARD_MEASURE_DELAYS_MS,
|
||||
KEYBOARD_POLL_INTERVAL_MS,
|
||||
KEYBOARD_RELEASE_GRACE_MS,
|
||||
KEYBOARD_VISIBLE_RATIO,
|
||||
KEYBOARD_VISIBLE_THRESHOLD_PX,
|
||||
} from "./constants";
|
||||
@@ -33,10 +32,7 @@ import {
|
||||
getKeyboardAdaptEnvironment,
|
||||
shouldEnableKeyboardAdaptation,
|
||||
} from "./environment";
|
||||
import {
|
||||
blurKeyboardTarget,
|
||||
isKeyboardTargetActive,
|
||||
} from "./keyboard-target";
|
||||
import { isKeyboardTargetActive } from "./keyboard-target";
|
||||
import type { UseKeyboardHeightOptions } from "./types";
|
||||
|
||||
const log = new Logger("HooksUseKeyboardHeight");
|
||||
@@ -51,16 +47,8 @@ interface KeyboardSnapshot {
|
||||
|
||||
export function useKeyboardHeight({
|
||||
targetRef,
|
||||
containerRef,
|
||||
active = false,
|
||||
onKeyboardDismiss,
|
||||
}: UseKeyboardHeightOptions = {}): void {
|
||||
const onKeyboardDismissRef = useRef(onKeyboardDismiss);
|
||||
|
||||
useEffect(() => {
|
||||
onKeyboardDismissRef.current = onKeyboardDismiss;
|
||||
}, [onKeyboardDismiss]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
@@ -77,10 +65,7 @@ export function useKeyboardHeight({
|
||||
let rafId: number | null = null;
|
||||
let pollId: number | null = null;
|
||||
let timeoutIds: number[] = [];
|
||||
let released = false;
|
||||
let keyboardWasDetected = false;
|
||||
let lastInputLift = -1;
|
||||
const focusedAt = active ? performance.now() : 0;
|
||||
|
||||
log.debug("[keyboard] environment", environment);
|
||||
|
||||
@@ -107,39 +92,18 @@ export function useKeyboardHeight({
|
||||
});
|
||||
};
|
||||
|
||||
const release = (reason: string) => {
|
||||
if (released) return;
|
||||
released = true;
|
||||
clearTimers();
|
||||
applyLift(0, reason);
|
||||
blurKeyboardTarget(targetRef?.current);
|
||||
onKeyboardDismissRef.current?.();
|
||||
log.debug("[keyboard] released", {
|
||||
reason,
|
||||
snapshot: formatSnapshot(readKeyboardSnapshot()),
|
||||
});
|
||||
};
|
||||
|
||||
const measure = (reason: string) => {
|
||||
rafId = null;
|
||||
|
||||
const target = targetRef?.current;
|
||||
const activeNow = isKeyboardTargetActive(target, active);
|
||||
const snapshot = readKeyboardSnapshot();
|
||||
if (snapshot.keyboardVisible) keyboardWasDetected = true;
|
||||
|
||||
if (!activeNow) {
|
||||
release("target-inactive");
|
||||
return;
|
||||
}
|
||||
|
||||
const focusedFor = focusedAt > 0 ? performance.now() - focusedAt : 0;
|
||||
if (
|
||||
keyboardWasDetected &&
|
||||
!snapshot.keyboardVisible &&
|
||||
focusedFor >= KEYBOARD_RELEASE_GRACE_MS
|
||||
) {
|
||||
release("viewport-restored");
|
||||
log.debug("[keyboard] skip inactive target", {
|
||||
reason,
|
||||
snapshot: formatSnapshot(snapshot),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,26 +127,7 @@ export function useKeyboardHeight({
|
||||
scheduleMeasure("viewport-signal");
|
||||
};
|
||||
|
||||
const handleFocusOut = () => {
|
||||
release("focusout");
|
||||
};
|
||||
|
||||
const handlePointerDown = (event: PointerEvent | TouchEvent) => {
|
||||
const target = targetRef?.current;
|
||||
const container = containerRef?.current;
|
||||
if (event.target instanceof Node && container?.contains(event.target)) {
|
||||
log.debug("[keyboard] keep active for input container tap", {
|
||||
snapshot: formatSnapshot(readKeyboardSnapshot()),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!target || event.target === target) return;
|
||||
if (event.target instanceof Node && target.contains(event.target)) return;
|
||||
release("outside-interaction");
|
||||
};
|
||||
|
||||
if (!active) {
|
||||
release("react-inactive");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,11 +136,7 @@ export function useKeyboardHeight({
|
||||
visualViewport?.addEventListener("scroll", handleViewportSignal);
|
||||
window.addEventListener("resize", handleViewportSignal);
|
||||
window.addEventListener("orientationchange", handleViewportSignal);
|
||||
window.addEventListener("focusout", handleFocusOut);
|
||||
window.addEventListener("blur", handleFocusOut);
|
||||
document.addEventListener("visibilitychange", handleViewportSignal);
|
||||
document.addEventListener("pointerdown", handlePointerDown, true);
|
||||
document.addEventListener("touchstart", handlePointerDown, true);
|
||||
|
||||
scheduleInitialMeasures();
|
||||
pollId = window.setInterval(
|
||||
@@ -210,18 +151,10 @@ export function useKeyboardHeight({
|
||||
visualViewport?.removeEventListener("scroll", handleViewportSignal);
|
||||
window.removeEventListener("resize", handleViewportSignal);
|
||||
window.removeEventListener("orientationchange", handleViewportSignal);
|
||||
window.removeEventListener("focusout", handleFocusOut);
|
||||
window.removeEventListener("blur", handleFocusOut);
|
||||
document.removeEventListener("visibilitychange", handleViewportSignal);
|
||||
document.removeEventListener("pointerdown", handlePointerDown, true);
|
||||
document.removeEventListener("touchstart", handlePointerDown, true);
|
||||
resetInputLiftVar();
|
||||
};
|
||||
}, [
|
||||
active,
|
||||
containerRef,
|
||||
targetRef,
|
||||
]);
|
||||
}, [active, targetRef]);
|
||||
}
|
||||
|
||||
function readKeyboardSnapshot(): KeyboardSnapshot {
|
||||
|
||||
@@ -4,23 +4,3 @@ export function isKeyboardTargetActive(
|
||||
): boolean {
|
||||
return reactActive || (target != null && document.activeElement === target);
|
||||
}
|
||||
|
||||
export function blurKeyboardTarget(
|
||||
target: HTMLElement | null | undefined,
|
||||
): void {
|
||||
if (!target || document.activeElement !== target) return;
|
||||
target.blur();
|
||||
}
|
||||
|
||||
export function getActiveElementTag(): string | null {
|
||||
const activeElement = document.activeElement;
|
||||
if (!activeElement) return null;
|
||||
|
||||
const id = activeElement.id ? `#${activeElement.id}` : "";
|
||||
const className =
|
||||
typeof activeElement.className === "string" && activeElement.className
|
||||
? `.${activeElement.className.split(/\s+/).filter(Boolean).join(".")}`
|
||||
: "";
|
||||
|
||||
return `${activeElement.tagName.toLowerCase()}${id}${className}`;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ import type { RefObject } from "react";
|
||||
|
||||
export interface UseKeyboardHeightOptions {
|
||||
targetRef?: RefObject<HTMLElement | null>;
|
||||
containerRef?: RefObject<HTMLElement | null>;
|
||||
active?: boolean;
|
||||
onKeyboardDismiss?: () => void;
|
||||
}
|
||||
|
||||
export interface KeyboardAdaptEnvironment {
|
||||
|
||||
Reference in New Issue
Block a user