Skip to content

Commit 6291268

Browse files
authored
fix: sync iOS keyboard viewport updates (#341)
1 parent b5ed52d commit 6291268

3 files changed

Lines changed: 43 additions & 5 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: patch
3+
---
4+
5+
Keep the mobile composer aligned to the live iOS keyboard height when switching keyboard modes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { getImmediateVisibleViewportHeight } from './useKeyboardHeight';
4+
5+
describe('getImmediateVisibleViewportHeight', () => {
6+
it('uses the saved keyboard height estimate on the first open', () => {
7+
expect(getImmediateVisibleViewportHeight(900, 640, 280, false)).toBe(620);
8+
});
9+
10+
it('falls back to the live viewport height when there is no saved estimate', () => {
11+
expect(getImmediateVisibleViewportHeight(900, 640, 0, false)).toBe(640);
12+
});
13+
14+
it('tracks the live viewport height once the keyboard CSS vars are active', () => {
15+
expect(getImmediateVisibleViewportHeight(900, 700, 280, true)).toBe(700);
16+
});
17+
});

src/app/hooks/ios-keyboard-fix/useKeyboardHeight.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ let mountCount = 0;
3838
// across instances.
3939
let cssVarsApplied = false;
4040

41+
export const getImmediateVisibleViewportHeight = (
42+
baselineHeight: number,
43+
viewportHeight: number,
44+
savedKeyboardHeight: number,
45+
varsAlreadyApplied: boolean
46+
): number => {
47+
if (varsAlreadyApplied) return viewportHeight;
48+
return savedKeyboardHeight > 0 ? baselineHeight - savedKeyboardHeight : viewportHeight;
49+
};
50+
4151
function isEditableElement(element: Element | null): boolean {
4252
if (!(element instanceof HTMLElement)) return false;
4353

@@ -131,11 +141,17 @@ export function useKeyboardHeight() {
131141
// immediate and stability-timer setCSSVars calls land on the same pixel
132142
// value — eliminating the second layout change that causes visible
133143
// timeline stutter during the keyboard animation.
134-
if (!cssVarsApplied) {
135-
const estimatedViewportHeight =
136-
sharedSavedHeight > 0 ? baselineHeight - sharedSavedHeight : viewport.height;
137-
setCSSVars(estimatedViewportHeight);
138-
}
144+
// Keep the CSS height pinned to the live viewport on every keyboard-mode
145+
// resize. The React state still waits for a stable value, but the layout
146+
// should not lag behind when iOS swaps between text and emoji keyboards.
147+
setCSSVars(
148+
getImmediateVisibleViewportHeight(
149+
baselineHeight,
150+
viewport.height,
151+
sharedSavedHeight,
152+
cssVarsApplied
153+
)
154+
);
139155

140156
// Cancel any document scroll iOS may have applied as scroll-prediction.
141157
if (window.scrollY !== 0) {

0 commit comments

Comments
 (0)