Skip to content

Fixed gap between input and keyboard (swiftkey keyboard)#1650

Merged
ragnep merged 4 commits intomainfrom
reverse-typing-bug
Dec 15, 2025
Merged

Fixed gap between input and keyboard (swiftkey keyboard)#1650
ragnep merged 4 commits intomainfrom
reverse-typing-bug

Conversation

@ragnep
Copy link
Copy Markdown
Contributor

@ragnep ragnep commented Dec 15, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Bottom navigation now reliably hides on Android when the on-screen keyboard appears; safe-area padding and layout spacing adjust to avoid gaps.
  • Performance
    • Keyboard visibility updates are debounced to prevent rapid state toggles and visual flicker.
  • Tests
    • Keyboard-related tests updated to reflect debounce behavior and ensure cleanup on unmount.

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: ragnep <ragneinfo@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 15, 2025

Warning

Rate limit exceeded

@ragnep has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 15 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 607fd7d and 3e512df.

📒 Files selected for processing (1)
  • __tests__/hooks/useAndroidKeyboard.test.ts (6 hunks)

Walkthrough

Debounced Android keyboard handling (50ms) was added to the keyboard hook; layout height calculation no longer subtracts keyboard height and now uses capacitorSpace; AppLayout and BottomNavigation gain keyboard-aware behavior (hide nav and adjust safe-area); tests updated to use fake timers and assert debounce semantics.

Changes

Cohort / File(s) Change Summary
Hook: Android keyboard debounce
hooks/useAndroidKeyboard.ts
Added 50ms debounced show/hide logic using refs and timeouts; replaced direct listener updates with debouncedShow/debouncedHide; added cleanup to clear pending timeouts and reset CSS variable on unmount.
Tests: debounce-aware keyboard tests
__tests__/hooks/useAndroidKeyboard.test.ts, __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
Tests switched to fake timers and promise-based listener setup; renamed and updated assertions to advance by DEBOUNCE_MS; added tests for cancellation, unmount cleanup, and debounce timing; assertion adjustments for LayoutContext regarding removed capSpace subtraction.
Layout: remove keyboardHeight from calculations
components/brain/my-stream/layout/LayoutContext.tsx
Removed keyboardHeight parameter from calculateHeightStyle and stopped subtracting keyboard height; use isKeyboardVisible to decide capSpace (0 when visible, reserved 128 when hidden); added Android-specific height transitions; updated memo deps.
App layout & navigation visibility
components/layout/AppLayout.tsx, components/navigation/BottomNavigation.tsx
AppLayout uses useAndroidKeyboard to compute shouldHideBottomNav and conditionally apply safe-area padding; BottomNavigation gained a public hidden?: boolean prop and applies opacity/transform transitions when hidden; notifications route resolution updated to use getNotificationsRoute(isApp).

Sequence Diagram(s)

sequenceDiagram
    participant Capacitor as Capacitor Plugin
    participant Hook as useAndroidKeyboard
    participant Timer as Debounce Timer
    participant State as Hook State / CSS Var
    participant Layout as LayoutContext
    participant UI as AppLayout / BottomNavigation

    rect rgb(200,220,240)
      note over Capacitor,Hook: Keyboard Show Event
      Capacitor->>Hook: keyboardWillShow(height)
      Hook->>Timer: schedule debouncedShow(height) (50ms)
      Timer->>Timer: wait DEBOUNCE_MS
      Timer->>State: set isVisible=true, keyboardHeight, update --android-keyboard-height
      State->>Layout: capSpace recalculated (capSpace=0 when visible)
      Layout->>UI: recalc height/styles -> re-render
    end

    rect rgb(240,220,200)
      note over Capacitor,Hook: Keyboard Hide Event
      Capacitor->>Hook: keyboardWillHide()
      Hook->>Timer: schedule debouncedHide() (50ms), cancel pending show if present
      Timer->>Timer: wait DEBOUNCE_MS
      Timer->>State: set isVisible=false, reset CSS var
      State->>Layout: capSpace updated (reserved 128 when hidden)
      Layout->>UI: recalc height/styles -> re-render (bottom nav may show)
    end

    rect rgb(220,240,220)
      note over Hook,Timer: Unmount Cleanup
      UI->>Hook: unmount
      Hook->>Timer: clear pending timeouts, reset CSS var
      Hook->>State: ensure cleaned state
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review debounce cancellation semantics in hooks/useAndroidKeyboard.ts (ensure opposing timers are reliably cleared).
  • Verify all callers of calculateHeightStyle and related layout logic align with removed keyboardHeight parameter in LayoutContext.tsx.
  • Confirm BottomNavigation.hidden prop interaction with accessibility and animations does not introduce layout/content shift regressions.
  • Validate tests' use of fake timers and promise-based listener setup to avoid flakiness.

Possibly related PRs

Suggested reviewers

  • GelatoGenesis
  • prxt6529

Poem

🐇✨
Fifty milliseconds hush the clatter,
Timers wait so layouts don't scatter,
No more cutting heights, just space in place,
Nav tucks away with a graceful face,
The rabbit nods — tidy UI, happy pace.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Fixed gap between input and keyboard (swiftkey keyboard)' refers to a specific visual/UX issue, but the actual changes involve comprehensive refactoring of keyboard handling logic, debouncing mechanisms, layout calculations, and bottom navigation visibility. Update the title to reflect the broader scope of changes, such as 'Refactor Android keyboard handling with debounce and layout adjustments' or similar, to accurately represent the main changes across hooks and components.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
components/layout/AppLayout.tsx (2)

52-54: Remove explanatory comments per coding guidelines.

The coding guidelines specify that code should be self-explanatory without comments. The variable name shouldHideBottomNav already conveys the intent clearly.

-  // Hide bottom nav when Android keyboard is open to prevent it sliding up with viewport
   const shouldHideBottomNav = isAndroid && isKeyboardVisible;
-

63-65: Remove comment and simplify the conditional expression.

Per coding guidelines, code should be self-explanatory. Consider a more descriptive variable name or inline the ternary.

-  // Remove safe-area padding when keyboard is open on Android (to avoid gap)
-  const safeAreaClass = shouldHideBottomNav ? "" : "tw-pb-[env(safe-area-inset-bottom,0px)]";
-
+  const safeAreaClass = shouldHideBottomNav
+    ? ""
+    : "tw-pb-[env(safe-area-inset-bottom,0px)]";
hooks/useAndroidKeyboard.ts (2)

34-64: Consider removing inline comments per coding guidelines.

The debounce logic is well-implemented with proper race condition prevention. However, per coding guidelines, code should be self-explanatory without comments. The function names debouncedShow and debouncedHide already convey intent.

-    // Debounced show handler - prevents rapid state updates from aggressive keyboards (e.g., SwiftKey)
     const debouncedShow = (height: number) => {
-      // Cancel any pending hide to prevent race conditions
       if (hideTimeoutRef.current) {
         clearTimeout(hideTimeoutRef.current);
         hideTimeoutRef.current = null;
       }
       if (showTimeoutRef.current) clearTimeout(showTimeoutRef.current);
       showTimeoutRef.current = setTimeout(() => {
         if (!mounted) return;
         setKeyboardHeight(height);
         setIsVisible(true);
         document.documentElement.style.setProperty('--android-keyboard-height', `${height}px`);
       }, DEBOUNCE_MS);
     };

-    // Debounced hide handler
     const debouncedHide = () => {
-      // Cancel any pending show to prevent race conditions
       if (showTimeoutRef.current) {
         clearTimeout(showTimeoutRef.current);
         showTimeoutRef.current = null;
       }
       if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
       hideTimeoutRef.current = setTimeout(() => {
         if (!mounted) return;
         setKeyboardHeight(0);
         setIsVisible(false);
         document.documentElement.style.setProperty('--android-keyboard-height', '0px');
       }, DEBOUNCE_MS);
     };

23-26: Remove explanatory comment.

Per coding guidelines, code should be self-explanatory. The variable names clearly indicate their purpose.

-  // Refs for debounce timeouts
   const showTimeoutRef = useRef<NodeJS.Timeout | null>(null);
   const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null);
-
components/brain/my-stream/layout/LayoutContext.tsx (1)

382-403: Consider removing explanatory comments per coding guidelines.

The implementation logic is correct - setting capSpace to 0 when the keyboard is visible eliminates the gap. The 75ms transition matches BottomNavigation for visual consistency.

-    // Reserve space for input area + bottom nav
-    // When keyboard is open on Android, bottom nav is hidden so we need less space
     let capSpace = 0;

     if (isAndroid) {
-      // 128px when keyboard closed (input visible), 0 when open (no gap)
       capSpace = isKeyboardVisible ? 0 : 128;
     } else if (isIos || isCapacitor) {
       capSpace = 20;
     }
__tests__/hooks/useAndroidKeyboard.test.ts (2)

1-1: Unused import: waitFor

The waitFor import appears to be unused after the refactor to use fake timers with jest.advanceTimersByTime().

-import { renderHook, act, waitFor } from '@testing-library/react';
+import { renderHook, act } from '@testing-library/react';

385-406: Consider adding an explicit assertion.

This test validates that no errors occur when timeouts fire after unmount, but it has no expect() statement. While Jest will pass tests that don't throw, adding an explicit assertion makes the test's intent clearer.

       // Advance time after unmount
       act(() => {
         jest.advanceTimersByTime(DEBOUNCE_MS * 2);
       });

-      // No errors should occur
+      // No errors should occur - test passes if no exception is thrown
+      expect(true).toBe(true);
     });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 395cdea and 5d0888c.

📒 Files selected for processing (6)
  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx (2 hunks)
  • __tests__/hooks/useAndroidKeyboard.test.ts (6 hunks)
  • components/brain/my-stream/layout/LayoutContext.tsx (3 hunks)
  • components/layout/AppLayout.tsx (4 hunks)
  • components/navigation/BottomNavigation.tsx (3 hunks)
  • hooks/useAndroidKeyboard.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • components/navigation/BottomNavigation.tsx
  • components/brain/my-stream/layout/LayoutContext.tsx
  • hooks/useAndroidKeyboard.ts
  • __tests__/hooks/useAndroidKeyboard.test.ts
  • components/layout/AppLayout.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • components/navigation/BottomNavigation.tsx
  • components/brain/my-stream/layout/LayoutContext.tsx
  • components/layout/AppLayout.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • components/navigation/BottomNavigation.tsx
  • components/brain/my-stream/layout/LayoutContext.tsx
  • hooks/useAndroidKeyboard.ts
  • __tests__/hooks/useAndroidKeyboard.test.ts
  • components/layout/AppLayout.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • components/navigation/BottomNavigation.tsx
  • components/brain/my-stream/layout/LayoutContext.tsx
  • hooks/useAndroidKeyboard.ts
  • __tests__/hooks/useAndroidKeyboard.test.ts
  • components/layout/AppLayout.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
🧠 Learnings (10)
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility

Applied to files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
  • components/layout/AppLayout.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label

Applied to files:

  • __tests__/components/brain/my-stream/layout/LayoutContext.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • hooks/useAndroidKeyboard.ts
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • hooks/useAndroidKeyboard.ts
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks

Applied to files:

  • hooks/useAndroidKeyboard.ts
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Test high-risk areas including happy path workflows, invalid input errors, edge cases/boundaries, component & API interactions, and performance/security when relevant

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
🧬 Code graph analysis (2)
components/brain/my-stream/layout/LayoutContext.tsx (1)
hooks/useAndroidKeyboard.ts (1)
  • useAndroidKeyboard (16-128)
__tests__/hooks/useAndroidKeyboard.test.ts (1)
hooks/useAndroidKeyboard.ts (1)
  • useAndroidKeyboard (16-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
components/layout/AppLayout.tsx (1)

84-86: LGTM!

The conditional rendering with the hidden prop correctly integrates with the new keyboard visibility logic, ensuring the bottom navigation is hidden when the Android keyboard is open.

components/navigation/BottomNavigation.tsx (2)

72-76: LGTM!

The props interface follows the coding guideline of using readonly for props, and the default value pattern is clean.


106-113: LGTM!

The visibility transition implementation is well-designed:

  • Uses tw-pointer-events-none to prevent interaction when hidden
  • The 75ms transition duration complements the 50ms debounce for a responsive feel
  • Transform-based hiding (tw-translate-y-full) is performant
__tests__/components/brain/my-stream/layout/LayoutContext.test.tsx (1)

85-100: LGTM!

The test correctly validates the updated behavior where capSpace is removed (set to 0) when the Android keyboard is visible. The assertions properly verify that - 128px is not present and - 0px) is included in the height calculation.

hooks/useAndroidKeyboard.ts (1)

94-101: LGTM!

The cleanup logic is thorough: it clears both debounce timeouts and resets the CSS variable, preventing memory leaks and stale state.

components/brain/my-stream/layout/LayoutContext.tsx (2)

65-76: LGTM!

The refactored calculateHeightStyle function with the explicit capacitorSpace parameter is cleaner and more intentional than the previous keyboard-height-based approach.


396-402: LGTM!

The Android-specific transition ensures smooth height adjustments when the keyboard state changes. The 75ms duration is consistent with the BottomNavigation component's transition timing.

__tests__/hooks/useAndroidKeyboard.test.ts (3)

28-51: LGTM!

The fake timer setup with proper teardown in afterEach and the promise-based mock for addListener correctly reflects the async nature of the Capacitor Keyboard API.


78-218: LGTM!

The debounce tests properly validate timing behavior including the race condition where a show event cancels a pending hide. The consistent pattern of awaiting async listener setup before triggering callbacks is correct.


238-346: LGTM!

The getContainerStyle tests provide comprehensive coverage including edge cases like negative transform prevention, existing transform combination, and transition preservation. The assertions correctly validate the transform calculation logic.

Comment thread __tests__/hooks/useAndroidKeyboard.test.ts
Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
__tests__/hooks/useAndroidKeyboard.test.ts (1)

4-5: Add a comment documenting the DEBOUNCE_MS timing dependency.

DEBOUNCE_MS is not exported from the implementation, so it cannot be imported. The test duplicates the constant value independently. Add a comment noting that this timing must stay synchronized with the implementation's DEBOUNCE_MS value to ensure tests remain valid if the debounce duration changes.

🧹 Nitpick comments (1)
__tests__/hooks/useAndroidKeyboard.test.ts (1)

385-405: Refactor the assertion to make test intent clearer.

The assertion expect(true).toBe(true) at line 404 is a no-op. The real test is that unmounting with pending timeouts doesn't throw. Consider restructuring to make the intent explicit.

Apply this diff:

-    it('clears pending timeouts on unmount', async () => {
+    it('safely clears pending timeouts on unmount without throwing', async () => {
       const { result, unmount } = renderHook(() => useAndroidKeyboard());
 
       await act(async () => {
         await Promise.resolve();
       });
 
-      // Start a show that hasn't completed
       act(() => {
         showCallback({ keyboardHeight: 350 });
-        // Don't advance time - timeout is pending
       });
 
-      unmount();
+      expect(() => unmount()).not.toThrow();
 
       act(() => {
         jest.advanceTimersByTime(DEBOUNCE_MS * 2);
       });
-
-      expect(true).toBe(true);
     });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d0888c and 607fd7d.

📒 Files selected for processing (4)
  • __tests__/hooks/useAndroidKeyboard.test.ts (6 hunks)
  • components/brain/my-stream/layout/LayoutContext.tsx (3 hunks)
  • components/layout/AppLayout.tsx (4 hunks)
  • hooks/useAndroidKeyboard.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/brain/my-stream/layout/LayoutContext.tsx
  • components/layout/AppLayout.tsx
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
  • hooks/useAndroidKeyboard.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
  • hooks/useAndroidKeyboard.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
  • hooks/useAndroidKeyboard.ts
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
🧠 Learnings (8)
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
  • hooks/useAndroidKeyboard.ts
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • __tests__/hooks/useAndroidKeyboard.test.ts
  • hooks/useAndroidKeyboard.ts
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • hooks/useAndroidKeyboard.ts
🧬 Code graph analysis (1)
__tests__/hooks/useAndroidKeyboard.test.ts (1)
hooks/useAndroidKeyboard.ts (1)
  • useAndroidKeyboard (16-123)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
hooks/useAndroidKeyboard.ts (4)

3-3: LGTM! Clean debounce setup.

The useRef import, DEBOUNCE_MS constant, and timeout refs are properly structured for implementing debounced keyboard handling.

Also applies to: 14-15, 23-24


33-59: LGTM! Robust debounce implementation.

The mutual cancellation logic ensures that rapid show/hide events don't cause race conditions. The mounted guard prevents state updates after unmount, and the CSS variable stays synchronized with the keyboard state.


67-67: LGTM! Proper listener integration and cleanup.

The keyboard event listeners correctly delegate to the debounced functions, and the cleanup logic ensures pending timeouts are cleared to prevent memory leaks.

Also applies to: 71-71, 93-94


99-120: LGTM! Container style logic is debounce-aware.

The getContainerStyle callback correctly uses isVisible and keyboardHeight in its dependencies, ensuring that style transforms align with the debounced keyboard state.

__tests__/hooks/useAndroidKeyboard.test.ts (5)

29-29: LGTM! Proper fake timer setup.

The fake timer setup and teardown follows Jest best practices, ensuring tests don't interfere with each other and timers are properly controlled for debounce testing.

Also applies to: 49-51


45-45: LGTM! Mock matches async implementation.

The mock correctly returns a Promise to match the async listener setup in the implementation.


78-93: LGTM! Comprehensive debounce testing.

The tests correctly wait for async listener setup, advance timers by the debounce period, and verify state transitions. Good coverage of show, hide, and fallback scenarios.

Also applies to: 95-119, 121-149


187-218: LGTM! Cancellation logic well-tested.

This test correctly verifies that a pending hide is cancelled when show is called mid-debounce, ensuring the mutual cancellation logic works as intended.


238-346: LGTM! Thorough testing of styles and cleanup.

The getContainerStyle tests comprehensively cover different keyboard states with proper debounce timing, and the cleanup tests verify that listeners are removed and CSS variables are reset correctly.

Also applies to: 349-383

Signed-off-by: ragnep <ragneinfo@gmail.com>
@sonarqubecloud
Copy link
Copy Markdown

@ragnep ragnep merged commit b4c0be4 into main Dec 15, 2025
8 checks passed
@ragnep ragnep deleted the reverse-typing-bug branch December 15, 2025 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants