Skip to content
Draft
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
6 changes: 6 additions & 0 deletions .changeset/LIVE-36689-edit-contact-keyboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@shared/ui-queued-bottom-sheet": patch
"live-mobile": patch
---

Fix the keyboard disappearing instantly when editing a contact name on Mobile. A bottom sheet retracts the keyboard when its close begins, but it also did so again when its dismissal finally landed β€” by then the sheet that replaced it (here, the rename drawer) had already raised the keyboard for its own field, so the late retraction stole it. A dismissal now only retracts the keyboard when it bypassed the close animation entirely.
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,45 @@ describe("useQueuedBottomSheet", () => {
expect(dismissKeyboard).toHaveBeenCalled();
});

it("retracts the keyboard when a dismissal bypasses the close animation", () => {
const dismissKeyboard = jest.spyOn(Keyboard, "dismiss");
jest.spyOn(Keyboard, "isVisible").mockReturnValue(true);
const { signalOpen } = setupBottomSheetStateCapture();

const { result } = renderHook(() => useQueuedBottomSheet({ isRequestingToBeOpened: true }));

signalOpen();

act(() => {
result.current.handleDismiss();
});

expect(dismissKeyboard).toHaveBeenCalled();
});

it("leaves the keyboard alone when the dismissal lands after the close already handled it", () => {
const dismissKeyboard = jest.spyOn(Keyboard, "dismiss");
const isKeyboardVisible = jest.spyOn(Keyboard, "isVisible").mockReturnValue(false);
const { signalOpen, signalClose } = setupBottomSheetStateCapture();

const { result } = renderHook(() => useQueuedBottomSheet({ isRequestingToBeOpened: true }));

signalOpen();
signalClose();

expect(dismissKeyboard).not.toHaveBeenCalled();

// The sheet that took over focuses its own input, so the keyboard is up again by the time this
// sheet's onDismiss finally arrives.
isKeyboardVisible.mockReturnValue(true);

act(() => {
result.current.handleDismiss();
});

expect(dismissKeyboard).not.toHaveBeenCalled();
});

it("does not reopen after dismiss when it is no longer requested (normal close)", () => {
const { signalOpen, signalClose } = setupBottomSheetStateCapture();
let isRequestingToBeOpened = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,12 @@ export function useQueuedBottomSheet({
const handleDismiss = useCallback(() => {
logBottomSheet("BottomSheet dismissed (onDismiss)");

dismissKeyboard();

// Fallback for dismissals that bypass the close animation (and thus handleAnimate).
// Fallback for dismissals that bypass the close animation (and thus handleAnimate). An
// orderly close already retracted the keyboard when it began, and onDismiss can land long
// after that β€” by then the sheet that took over may have raised the keyboard for its own
// input, and retracting it again would steal it.
if (stateRef.current === "open") {
dismissKeyboard();
onCloseRef.current?.();
}

Expand Down
Loading