From b0c36383a9a7304f94c2bb19f7cc4b37e0b4f637 Mon Sep 17 00:00:00 2001
From: Samuel Newman <mozzius@protonmail.com>
Date: Mon, 25 Nov 2024 20:30:15 +0000
Subject: Ensure react-native-keyboard-controller enabled state doesn't get
 overwritten (#6727)

* revert to prev state instead of false

* add dep array

* use ref counting approach

* patch keyboard controller to allow changing the enabled prop

* remove state from patch

* change patched prop name

* remove Math.max check, log if < 0

* use noop provider

* rm patch, use `useRef`

* Style nits

* Rm on web

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
---
 src/lib/hooks/useEnableKeyboardController.tsx | 110 ++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 src/lib/hooks/useEnableKeyboardController.tsx

(limited to 'src/lib/hooks/useEnableKeyboardController.tsx')

diff --git a/src/lib/hooks/useEnableKeyboardController.tsx b/src/lib/hooks/useEnableKeyboardController.tsx
new file mode 100644
index 000000000..c7205d016
--- /dev/null
+++ b/src/lib/hooks/useEnableKeyboardController.tsx
@@ -0,0 +1,110 @@
+import {
+  createContext,
+  useCallback,
+  useContext,
+  useEffect,
+  useMemo,
+  useRef,
+} from 'react'
+import {
+  KeyboardProvider,
+  useKeyboardController,
+} from 'react-native-keyboard-controller'
+import {useFocusEffect} from '@react-navigation/native'
+
+import {IS_DEV} from '#/env'
+
+const KeyboardControllerRefCountContext = createContext<{
+  incrementRefCount: () => void
+  decrementRefCount: () => void
+}>({
+  incrementRefCount: () => {},
+  decrementRefCount: () => {},
+})
+
+export function KeyboardControllerProvider({
+  children,
+}: {
+  children: React.ReactNode
+}) {
+  return (
+    <KeyboardProvider
+      enabled={false}
+      // I don't think this is necessary, but Chesterton's fence and all that -sfn
+      statusBarTranslucent={true}>
+      <KeyboardControllerProviderInner>
+        {children}
+      </KeyboardControllerProviderInner>
+    </KeyboardProvider>
+  )
+}
+
+function KeyboardControllerProviderInner({
+  children,
+}: {
+  children: React.ReactNode
+}) {
+  const {setEnabled} = useKeyboardController()
+  const refCount = useRef(0)
+
+  const value = useMemo(
+    () => ({
+      incrementRefCount: () => {
+        refCount.current++
+        setEnabled(refCount.current > 0)
+      },
+      decrementRefCount: () => {
+        refCount.current--
+        setEnabled(refCount.current > 0)
+
+        if (IS_DEV && refCount.current < 0) {
+          console.error('KeyboardController ref count < 0')
+        }
+      },
+    }),
+    [setEnabled],
+  )
+
+  return (
+    <KeyboardControllerRefCountContext.Provider value={value}>
+      {children}
+    </KeyboardControllerRefCountContext.Provider>
+  )
+}
+
+export function useEnableKeyboardController(shouldEnable: boolean) {
+  const {incrementRefCount, decrementRefCount} = useContext(
+    KeyboardControllerRefCountContext,
+  )
+
+  useEffect(() => {
+    if (!shouldEnable) {
+      return
+    }
+    incrementRefCount()
+    return () => {
+      decrementRefCount()
+    }
+  }, [shouldEnable, incrementRefCount, decrementRefCount])
+}
+
+/**
+ * Like `useEnableKeyboardController`, but using `useFocusEffect`
+ */
+export function useEnableKeyboardControllerScreen(shouldEnable: boolean) {
+  const {incrementRefCount, decrementRefCount} = useContext(
+    KeyboardControllerRefCountContext,
+  )
+
+  useFocusEffect(
+    useCallback(() => {
+      if (!shouldEnable) {
+        return
+      }
+      incrementRefCount()
+      return () => {
+        decrementRefCount()
+      }
+    }, [shouldEnable, incrementRefCount, decrementRefCount]),
+  )
+}
-- 
cgit 1.4.1

