about summary refs log tree commit diff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/hooks/useDedupe.ts17
-rw-r--r--src/lib/hooks/useNavigationDeduped.ts80
-rw-r--r--src/lib/hooks/useOTAUpdate.ts52
-rw-r--r--src/lib/statsig/statsig.web.tsx75
4 files changed, 112 insertions, 112 deletions
diff --git a/src/lib/hooks/useDedupe.ts b/src/lib/hooks/useDedupe.ts
new file mode 100644
index 000000000..d9432cb2c
--- /dev/null
+++ b/src/lib/hooks/useDedupe.ts
@@ -0,0 +1,17 @@
+import React from 'react'
+
+export const useDedupe = () => {
+  const canDo = React.useRef(true)
+
+  return React.useRef((cb: () => unknown) => {
+    if (canDo.current) {
+      canDo.current = false
+      setTimeout(() => {
+        canDo.current = true
+      }, 250)
+      cb()
+      return true
+    }
+    return false
+  }).current
+}
diff --git a/src/lib/hooks/useNavigationDeduped.ts b/src/lib/hooks/useNavigationDeduped.ts
new file mode 100644
index 000000000..d913f7f3d
--- /dev/null
+++ b/src/lib/hooks/useNavigationDeduped.ts
@@ -0,0 +1,80 @@
+import React from 'react'
+import {useNavigation} from '@react-navigation/core'
+import {AllNavigatorParams, NavigationProp} from 'lib/routes/types'
+import type {NavigationAction} from '@react-navigation/routers'
+import {NavigationState} from '@react-navigation/native'
+import {useDedupe} from 'lib/hooks/useDedupe'
+
+export type DebouncedNavigationProp = Pick<
+  NavigationProp,
+  | 'popToTop'
+  | 'push'
+  | 'navigate'
+  | 'canGoBack'
+  | 'replace'
+  | 'dispatch'
+  | 'goBack'
+  | 'getState'
+>
+
+export function useNavigationDeduped() {
+  const navigation = useNavigation<NavigationProp>()
+  const dedupe = useDedupe()
+
+  return React.useMemo(
+    (): DebouncedNavigationProp => ({
+      // Types from @react-navigation/routers/lib/typescript/src/StackRouter.ts
+      push: <RouteName extends keyof AllNavigatorParams>(
+        ...args: undefined extends AllNavigatorParams[RouteName]
+          ?
+              | [screen: RouteName]
+              | [screen: RouteName, params: AllNavigatorParams[RouteName]]
+          : [screen: RouteName, params: AllNavigatorParams[RouteName]]
+      ) => {
+        dedupe(() => navigation.push(...args))
+      },
+      // Types from @react-navigation/core/src/types.tsx
+      navigate: <RouteName extends keyof AllNavigatorParams>(
+        ...args: RouteName extends unknown
+          ? undefined extends AllNavigatorParams[RouteName]
+            ?
+                | [screen: RouteName]
+                | [screen: RouteName, params: AllNavigatorParams[RouteName]]
+            : [screen: RouteName, params: AllNavigatorParams[RouteName]]
+          : never
+      ) => {
+        dedupe(() => navigation.navigate(...args))
+      },
+      // Types from @react-navigation/routers/lib/typescript/src/StackRouter.ts
+      replace: <RouteName extends keyof AllNavigatorParams>(
+        ...args: undefined extends AllNavigatorParams[RouteName]
+          ?
+              | [screen: RouteName]
+              | [screen: RouteName, params: AllNavigatorParams[RouteName]]
+          : [screen: RouteName, params: AllNavigatorParams[RouteName]]
+      ) => {
+        dedupe(() => navigation.replace(...args))
+      },
+      dispatch: (
+        action:
+          | NavigationAction
+          | ((state: NavigationState) => NavigationAction),
+      ) => {
+        dedupe(() => navigation.dispatch(action))
+      },
+      popToTop: () => {
+        dedupe(() => navigation.popToTop())
+      },
+      goBack: () => {
+        dedupe(() => navigation.goBack())
+      },
+      canGoBack: () => {
+        return navigation.canGoBack()
+      },
+      getState: () => {
+        return navigation.getState()
+      },
+    }),
+    [dedupe, navigation],
+  )
+}
diff --git a/src/lib/hooks/useOTAUpdate.ts b/src/lib/hooks/useOTAUpdate.ts
index 53eab300e..d35179256 100644
--- a/src/lib/hooks/useOTAUpdate.ts
+++ b/src/lib/hooks/useOTAUpdate.ts
@@ -2,25 +2,9 @@ import * as Updates from 'expo-updates'
 import {useCallback, useEffect} from 'react'
 import {AppState} from 'react-native'
 import {logger} from '#/logger'
-import {useModalControls} from '#/state/modals'
-import {t} from '@lingui/macro'
 
 export function useOTAUpdate() {
-  const {openModal} = useModalControls()
-
   // HELPER FUNCTIONS
-  const showUpdatePopup = useCallback(() => {
-    openModal({
-      name: 'confirm',
-      title: t`Update Available`,
-      message: t`A new version of the app is available. Please update to continue using the app.`,
-      onPressConfirm: async () => {
-        Updates.reloadAsync().catch(err => {
-          throw err
-        })
-      },
-    })
-  }, [openModal])
   const checkForUpdate = useCallback(async () => {
     logger.debug('useOTAUpdate: Checking for update...')
     try {
@@ -32,32 +16,26 @@ export function useOTAUpdate() {
       }
       // Otherwise fetch the update in the background, so even if the user rejects switching to latest version it will be done automatically on next relaunch.
       await Updates.fetchUpdateAsync()
-      // show a popup modal
-      showUpdatePopup()
     } catch (e) {
       logger.error('useOTAUpdate: Error while checking for update', {
         message: e,
       })
     }
-  }, [showUpdatePopup])
-  const updateEventListener = useCallback(
-    (event: Updates.UpdateEvent) => {
-      logger.debug('useOTAUpdate: Listening for update...')
-      if (event.type === Updates.UpdateEventType.ERROR) {
-        logger.error('useOTAUpdate: Error while listening for update', {
-          message: event.message,
-        })
-      } else if (event.type === Updates.UpdateEventType.NO_UPDATE_AVAILABLE) {
-        // Handle no update available
-        // do nothing
-      } else if (event.type === Updates.UpdateEventType.UPDATE_AVAILABLE) {
-        // Handle update available
-        // open modal, ask for user confirmation, and reload the app
-        showUpdatePopup()
-      }
-    },
-    [showUpdatePopup],
-  )
+  }, [])
+  const updateEventListener = useCallback((event: Updates.UpdateEvent) => {
+    logger.debug('useOTAUpdate: Listening for update...')
+    if (event.type === Updates.UpdateEventType.ERROR) {
+      logger.error('useOTAUpdate: Error while listening for update', {
+        message: event.message,
+      })
+    } else if (event.type === Updates.UpdateEventType.NO_UPDATE_AVAILABLE) {
+      // Handle no update available
+      // do nothing
+    } else if (event.type === Updates.UpdateEventType.UPDATE_AVAILABLE) {
+      // Handle update available
+      // open modal, ask for user confirmation, and reload the app
+    }
+  }, [])
 
   useEffect(() => {
     // ADD EVENT LISTENERS
diff --git a/src/lib/statsig/statsig.web.tsx b/src/lib/statsig/statsig.web.tsx
deleted file mode 100644
index d1c912019..000000000
--- a/src/lib/statsig/statsig.web.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React from 'react'
-import {
-  Statsig,
-  StatsigProvider,
-  useGate as useStatsigGate,
-} from 'statsig-react'
-import {useSession} from '../../state/session'
-import {sha256} from 'js-sha256'
-
-const statsigOptions = {
-  environment: {
-    tier: process.env.NODE_ENV === 'development' ? 'development' : 'production',
-  },
-  // Don't block on waiting for network. The fetched config will kick in on next load.
-  // This ensures the UI is always consistent and doesn't update mid-session.
-  // Note this makes cold load (no local storage) and private mode return `false` for all gates.
-  initTimeoutMs: 1,
-}
-
-export function logEvent(
-  eventName: string,
-  value?: string | number | null,
-  metadata?: Record<string, string> | null,
-) {
-  Statsig.logEvent(eventName, value, metadata)
-}
-
-export function useGate(gateName: string) {
-  const {isLoading, value} = useStatsigGate(gateName)
-  if (isLoading) {
-    // This should not happen because of waitForInitialization={true}.
-    console.error('Did not expected isLoading to ever be true.')
-  }
-  return value
-}
-
-function toStatsigUser(did: string | undefined) {
-  let userID: string | undefined
-  if (did) {
-    userID = sha256(did)
-  }
-  return {userID}
-}
-
-export function Provider({children}: {children: React.ReactNode}) {
-  const {currentAccount} = useSession()
-  const currentStatsigUser = React.useMemo(
-    () => toStatsigUser(currentAccount?.did),
-    [currentAccount?.did],
-  )
-
-  React.useEffect(() => {
-    function refresh() {
-      // Intentionally refetching the config using the JS SDK rather than React SDK
-      // so that the new config is stored in cache but isn't used during this session.
-      // It will kick in for the next reload.
-      Statsig.updateUser(currentStatsigUser)
-    }
-    const id = setInterval(refresh, 3 * 60e3 /* 3 min */)
-    return () => clearInterval(id)
-  }, [currentStatsigUser])
-
-  return (
-    <StatsigProvider
-      sdkKey="client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV"
-      mountKey={currentStatsigUser.userID}
-      user={currentStatsigUser}
-      // This isn't really blocking due to short initTimeoutMs above.
-      // However, it ensures `isLoading` is always `false`.
-      waitForInitialization={true}
-      options={statsigOptions}>
-      {children}
-    </StatsigProvider>
-  )
-}