diff --git a/.eslintrc.js b/.eslintrc.js
index 6e6ad9d984b..d1c27a5af5f 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -317,6 +317,7 @@ module.exports = {
['types', './src/types'],
['ui', './src/ui'],
['web', './src/web'],
+ ['events', './src/events'],
],
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.mjs'],
},
diff --git a/package.json b/package.json
index ce378394341..17d000abeb7 100644
--- a/package.json
+++ b/package.json
@@ -132,6 +132,7 @@
"date-fns-tz": "^2.0.0",
"deprecated-react-native-prop-types": "^5.0.0",
"design-system": "https://github.com/pass-culture/design-system.git#v0.0.14",
+ "eventemitter3": "^5.0.1",
"firebase": "^9.6.11",
"geojson": "^0.5.0",
"globalthis": "^1.0.2",
diff --git a/src/App.tsx b/src/App.tsx
index f863ed0ddb2..50416fe273b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,10 +1,11 @@
import React, { FunctionComponent, useEffect } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
-import 'react-native-gesture-handler' // @react-navigation
-import 'react-native-get-random-values' // required for `uuid` module to work
import { LogBox, Platform, StatusBar } from 'react-native'
import CodePush from 'react-native-code-push'
+import { onlineManager } from 'react-query'
+import 'react-native-get-random-values' // required for `uuid` module to work
+import 'react-native-gesture-handler' // @react-navigation
// if __DEV__ import if you want to debug
// import './why-did-you-render'
if (process.env.NODE_ENV === 'development') {
@@ -13,6 +14,14 @@ if (process.env.NODE_ENV === 'development') {
import 'intl'
import 'intl/locale-data/jsonp/en'
+import { eventBus } from 'events/eventBus'
+import { EventBusProvider } from 'events/EventBusProvider'
+import {
+ NAVIGATION_EVENTS,
+ NETWORK_EVENTS,
+ SNACKBAR_EVENTS,
+ SPLASHSCREEN_EVENTS,
+} from 'events/eventNames'
import { AccessibilityFiltersWrapper } from 'features/accessibility/context/AccessibilityFiltersWrapper'
import { AuthWrapper } from 'features/auth/context/AuthWrapper'
import { SettingsWrapper } from 'features/auth/context/SettingsContext'
@@ -81,53 +90,55 @@ const App: FunctionComponent = function () {
}, [])
return (
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
)
}
@@ -139,6 +150,25 @@ const AppWithMonitoring = eventMonitoring.wrap(AppWithoutMonitoring) as React.Co
}>
const AppWithCodepush = __DEV__ ? AppWithMonitoring : CodePush(config)(AppWithMonitoring)
+// SIDE EFFECTS
+eventBus.on(NETWORK_EVENTS.OFFLINE, () => {
+ onlineManager.setOnline(false)
+ eventBus.emit(SPLASHSCREEN_EVENTS.HIDE)
+ eventBus.emit(SNACKBAR_EVENTS.SHOW, {
+ type: 'info',
+ message: 'Aucune connexion internet. RĂ©essaie plus tard',
+ })
+})
+
+eventBus.on(NETWORK_EVENTS.ONLINE, () => {
+ onlineManager.setOnline(true)
+ eventBus.emit(SNACKBAR_EVENTS.HIDE)
+})
+
+eventBus.once(NAVIGATION_EVENTS.READY, () => {
+ eventBus.emit(SPLASHSCREEN_EVENTS.HIDE)
+})
+
/**
* We have an import bug in the test file App.native.test.tsx with the new eventMonitoring wrapper : WEIRD !!! :
* Element type is invalid: expected a string (for built-in components) or a class/function (for composite components)
diff --git a/src/events/EventBusContext.tsx b/src/events/EventBusContext.tsx
new file mode 100644
index 00000000000..0a1e33c7dfe
--- /dev/null
+++ b/src/events/EventBusContext.tsx
@@ -0,0 +1,4 @@
+import EventEmitter from 'eventemitter3'
+import React from 'react'
+
+export const EventBusContext = React.createContext(new EventEmitter())
diff --git a/src/events/EventBusProvider.tsx b/src/events/EventBusProvider.tsx
new file mode 100644
index 00000000000..24cf9632f05
--- /dev/null
+++ b/src/events/EventBusProvider.tsx
@@ -0,0 +1,8 @@
+import React, { PropsWithChildren } from 'react'
+
+import { eventBus } from './eventBus'
+import { EventBusContext } from './EventBusContext'
+
+export const EventBusProvider = ({ children }: PropsWithChildren) => {
+ return {children}
+}
diff --git a/src/events/eventBus.ts b/src/events/eventBus.ts
new file mode 100644
index 00000000000..f63d5882f3e
--- /dev/null
+++ b/src/events/eventBus.ts
@@ -0,0 +1,3 @@
+import EventEmitter from 'eventemitter3'
+
+export const eventBus = new EventEmitter()
diff --git a/src/events/eventNames.ts b/src/events/eventNames.ts
new file mode 100644
index 00000000000..1d4ac42a0bd
--- /dev/null
+++ b/src/events/eventNames.ts
@@ -0,0 +1,18 @@
+export const SPLASHSCREEN_EVENTS = {
+ HIDE: 'splashscreen.hide',
+ SHOW: 'splashscreen.show',
+}
+
+export const NETWORK_EVENTS = {
+ ONLINE: 'network.online',
+ OFFLINE: 'network.offline',
+}
+
+export const SNACKBAR_EVENTS = {
+ SHOW: 'snackbar.show',
+ HIDE: 'snackbar.hide',
+}
+
+export const NAVIGATION_EVENTS = {
+ READY: 'navigation.ready',
+}
diff --git a/src/events/types.ts b/src/events/types.ts
new file mode 100644
index 00000000000..4e9b6c3e446
--- /dev/null
+++ b/src/events/types.ts
@@ -0,0 +1,4 @@
+export type SnackBarEventPayload = {
+ type: 'info' | 'success' | 'error'
+ message: string
+}
diff --git a/src/events/useEventBus.ts b/src/events/useEventBus.ts
new file mode 100644
index 00000000000..cd2862be0b2
--- /dev/null
+++ b/src/events/useEventBus.ts
@@ -0,0 +1,5 @@
+import { useContext } from 'react'
+
+import { EventBusContext } from './EventBusContext'
+
+export const useEventBus = () => useContext(EventBusContext)
diff --git a/src/features/auth/context/AuthContext.native.test.tsx b/src/features/auth/context/AuthContext.native.test.tsx
index f46bd2c3070..478bcfec0f3 100644
--- a/src/features/auth/context/AuthContext.native.test.tsx
+++ b/src/features/auth/context/AuthContext.native.test.tsx
@@ -3,7 +3,7 @@ import React from 'react'
import { BatchProfile } from '__mocks__/@batch.com/react-native-plugin'
import * as jwt from '__mocks__/jwt-decode'
-import { UserProfileResponse } from 'api/gen'
+import { RefreshResponse, UserProfileResponse } from 'api/gen'
import { CURRENT_DATE } from 'features/auth/fixtures/fixtures'
import * as NavigationRef from 'features/navigation/navigationRef'
import { beneficiaryUser, nonBeneficiaryUser } from 'fixtures/user'
@@ -12,8 +12,7 @@ import { amplitude } from 'libs/amplitude'
import { decodedTokenWithRemainingLifetime, tokenRemainingLifetimeInMs } from 'libs/jwt/fixtures'
import { clearRefreshToken, getRefreshToken, saveRefreshToken } from 'libs/keychain/keychain'
import { eventMonitoring } from 'libs/monitoring'
-import { NetInfoWrapper } from 'libs/network/NetInfoWrapper'
-import { useNetInfo } from 'libs/network/useNetInfo'
+import { useNetInfoContext as useNetInfoContextDefault } from 'libs/network/NetInfoWrapper'
import * as PackageJson from 'libs/packageJson'
import { QueryKeys } from 'libs/queryKeys'
import { StorageKey, storage } from 'libs/storage'
@@ -24,7 +23,8 @@ import { act, renderHook } from 'tests/utils'
import { useAuthContext } from './AuthContext'
import { AuthWrapper } from './AuthWrapper'
-const mockedUseNetInfo = useNetInfo as jest.Mock
+jest.mock('libs/network/NetInfoWrapper')
+const mockUseNetInfoContext = useNetInfoContextDefault as jest.Mock
jest.mock('libs/amplitude/amplitude')
@@ -54,17 +54,25 @@ describe('AuthContext', () => {
describe('useAuthContext', () => {
beforeEach(() => {
mockServer.getApi('/v1/me', nonBeneficiaryUser)
+ mockServer.postApi('/v1/refresh_access_token', {})
+ mockUseNetInfoContext.mockReturnValue({
+ isConnected: true,
+ isInternetReachable: true,
+ })
})
it('should not return user when logged in but no internet connection', async () => {
- mockedUseNetInfo.mockReturnValueOnce({ isConnected: false, isInternetReachable: false })
+ mockUseNetInfoContext.mockReturnValueOnce({
+ isConnected: false,
+ isInternetReachable: false,
+ })
await saveRefreshToken('token')
const result = renderUseAuthContext()
await act(async () => {})
- expect(result.current).toBeNull()
+ expect(result.current.user).toEqual({})
})
it('should return the user when logged in with internet connection', async () => {
@@ -278,12 +286,7 @@ describe('AuthContext', () => {
const renderUseAuthContext = () => {
const { result } = renderHook(useAuthContext, {
- wrapper: ({ children }) =>
- reactQueryProviderHOC(
-
- {children}
-
- ),
+ wrapper: ({ children }) => reactQueryProviderHOC({children}),
})
return result
diff --git a/src/features/navigation/NavigationContainer/NavigationContainer.tsx b/src/features/navigation/NavigationContainer/NavigationContainer.tsx
index 43f708faaf9..3f56cf24652 100644
--- a/src/features/navigation/NavigationContainer/NavigationContainer.tsx
+++ b/src/features/navigation/NavigationContainer/NavigationContainer.tsx
@@ -8,9 +8,11 @@ import React, { useEffect, useState } from 'react'
import { Platform } from 'react-native'
import { DefaultTheme, useTheme } from 'styled-components/native'
+import { NAVIGATION_EVENTS } from 'events/eventNames'
+import { useEventBus } from 'events/useEventBus'
import { RootNavigator } from 'features/navigation/RootNavigator'
import { linking } from 'features/navigation/RootNavigator/linking'
-import { useSplashScreenContext } from 'libs/splashscreen'
+import { useInitialScreen } from 'features/navigation/RootNavigator/useInitialScreenConfig'
import { storage } from 'libs/storage'
import { LoadingPage } from 'ui/components/LoadingPage'
@@ -33,12 +35,15 @@ const DOCUMENT_TITLE_OPTIONS: DocumentTitleOptions = {
}
export const AppNavigationContainer = () => {
- const { hideSplashScreen } = useSplashScreenContext()
const theme = useTheme()
+ const eventBus = useEventBus()
const [isNavReady, setIsNavReady] = useState(false)
+ const [navigationStateLoaded, setNavigationStateLoaded] = useState(false)
const [initialNavigationState, setInitialNavigationState] = useState()
+ const initialScreen = useInitialScreen()
+
useEffect(() => {
async function restoreNavStateOnReload() {
try {
@@ -50,34 +55,29 @@ export const AppNavigationContainer = () => {
setInitialNavigationState(savedState)
} finally {
- setIsNavReady(true)
+ setNavigationStateLoaded(true)
}
}
restoreNavStateOnReload()
}, [])
- useEffect(() => {
- if (isNavReady) {
- hideSplashScreen?.()
- }
- }, [isNavReady, hideSplashScreen])
-
- if (!isNavReady) {
- return
+ if (isNavReady && navigationStateLoaded && initialScreen) {
+ eventBus.emit(NAVIGATION_EVENTS.READY)
}
- return (
+ return initialScreen ? (
setIsNavReady(true)}
fallback={}
ref={navigationRef}
documentTitle={DOCUMENT_TITLE_OPTIONS}
theme={getNavThemeConfig(theme)}>
-
+
- )
+ ) : null
}
export default AppNavigationContainer
diff --git a/src/features/navigation/RootNavigator/RootNavigator.tsx b/src/features/navigation/RootNavigator/RootNavigator.tsx
index 9a84b157645..f806530a135 100644
--- a/src/features/navigation/RootNavigator/RootNavigator.tsx
+++ b/src/features/navigation/RootNavigator/RootNavigator.tsx
@@ -10,7 +10,6 @@ import { CheatcodesStackNavigator } from 'features/navigation/CheatcodesStackNav
import { useCurrentRoute } from 'features/navigation/helpers/useCurrentRoute'
import { AccessibleTabBar } from 'features/navigation/RootNavigator/Header/AccessibleTabBar'
import { RootScreenNames } from 'features/navigation/RootNavigator/types'
-import { useInitialScreen } from 'features/navigation/RootNavigator/useInitialScreenConfig'
import { withWebWrapper } from 'features/navigation/RootNavigator/withWebWrapper'
import { TabNavigationStateProvider } from 'features/navigation/TabBar/TabNavigationStateContext'
import { VenueMapFiltersStackNavigator } from 'features/navigation/VenueMapFiltersStackNavigator/VenueMapFiltersStackNavigator'
@@ -18,7 +17,6 @@ import { AccessibilityRole } from 'libs/accessibilityRole/accessibilityRole'
import { useSplashScreenContext } from 'libs/splashscreen'
import { storage } from 'libs/storage'
import { IconFactoryProvider } from 'ui/components/icons/IconFactoryProvider'
-import { LoadingPage } from 'ui/components/LoadingPage'
import { QuickAccess } from 'ui/web/link/QuickAccess'
import { determineAccessibilityRole } from './determineAccessibilityRole'
@@ -31,7 +29,7 @@ import { RootStack } from './Stack'
const isWeb = Platform.OS === 'web'
const RootStackNavigator = withWebWrapper(
- ({ initialRouteName }: { initialRouteName: RootScreenNames }) => {
+ ({ initialRouteName }: { initialRouteName?: RootScreenNames }) => {
const { top } = useSafeAreaInsets()
return (
@@ -61,15 +59,13 @@ const RootStackNavigator = withWebWrapper(
}
)
-export const RootNavigator: React.ComponentType = () => {
+export const RootNavigator = ({ initialScreen }: { initialScreen?: RootScreenNames }) => {
const mainId = uuidv4()
const tabBarId = uuidv4()
const { showTabBar } = useTheme()
const { isLoggedIn } = useAuthContext()
const { isSplashScreenHidden } = useSplashScreenContext()
- const initialScreen = useInitialScreen()
-
const currentRoute = useCurrentRoute()
const showHeaderQuickAccess = currentRoute && currentRoute.name === 'TabNavigator'
const headerWithQuickAccess = showHeaderQuickAccess ? (
@@ -88,10 +84,6 @@ export const RootNavigator: React.ComponentType = () => {
}
}, [isLoggedIn])
- if (!initialScreen) {
- return
- }
-
const mainAccessibilityRole: AccessibilityRole | undefined =
determineAccessibilityRole(currentRoute)
diff --git a/src/libs/network/NetInfoWrapper.native.test.tsx b/src/libs/network/NetInfoWrapper.native.test.tsx
index 0ad982e3ddf..9bae7a367ff 100644
--- a/src/libs/network/NetInfoWrapper.native.test.tsx
+++ b/src/libs/network/NetInfoWrapper.native.test.tsx
@@ -3,22 +3,35 @@ import { NetInfoStateType } from '@react-native-community/netinfo'
import React from 'react'
import { View } from 'react-native'
+import { NETWORK_EVENTS } from 'events/eventNames'
+import * as useEventBus from 'events/useEventBus'
import { analytics } from 'libs/analytics'
import { NetInfoWrapper, useNetInfoContext } from 'libs/network/NetInfoWrapper'
import { useNetInfo } from 'libs/network/useNetInfo'
-import { useSplashScreenContext } from 'libs/splashscreen'
import { render, screen } from 'tests/utils'
const mockedUseNetInfo = useNetInfo as unknown as jest.Mock<{
- isConnected: boolean
- isInternetReachable: boolean
+ isConnected: boolean | null
+ isInternetReachable: boolean | null
type: NetInfoStateType
details?: Record
}>
-jest.mock('libs/splashscreen')
-const mockUseSplashScreenContext = useSplashScreenContext as jest.Mock
-mockUseSplashScreenContext.mockReturnValue({ isSplashScreenHidden: true })
+const mockEventBus = {
+ on: jest.fn(),
+ off: jest.fn(),
+ emit: jest.fn(),
+ addListener: jest.fn(),
+ listeners: jest.fn(),
+ listenerCount: jest.fn(),
+ once: jest.fn(),
+ removeListener: jest.fn(),
+ removeAllListeners: jest.fn(),
+ eventNames: jest.fn(),
+}
+
+const useEventBusSpy = jest.spyOn(useEventBus, 'useEventBus')
+useEventBusSpy.mockReturnValue(mockEventBus)
describe('NetInfoWrapper', () => {
describe('useNetInfoContext', () => {
@@ -39,23 +52,6 @@ describe('NetInfoWrapper', () => {
expect(onConnectionLost).not.toHaveBeenCalled()
})
- it('should call onConnectionLost', () => {
- mockedUseNetInfo.mockReturnValueOnce({
- isConnected: false,
- isInternetReachable: true,
- type: NetInfoStateType.wifi,
- })
- renderNetInfoWrapper({
- onInternetConnection,
- onInternetConnectionLost,
- onConnection,
- onConnectionLost,
- })
-
- expect(onConnection).not.toHaveBeenCalled()
- expect(onConnectionLost).toHaveBeenCalledTimes(1)
- })
-
it('should call onInternetConnection', () => {
mockedUseNetInfo.mockReturnValueOnce({
isConnected: true,
@@ -69,14 +65,15 @@ describe('NetInfoWrapper', () => {
onConnectionLost,
})
+ expect(mockEventBus.emit).toHaveBeenCalledWith(NETWORK_EVENTS.ONLINE)
expect(onInternetConnection).toHaveBeenCalledTimes(1)
expect(onInternetConnectionLost).not.toHaveBeenCalled()
})
- it('should call onInternetConnectionLost', () => {
+ it('should log network information when wifi is used', async () => {
mockedUseNetInfo.mockReturnValueOnce({
- isConnected: true,
- isInternetReachable: false,
+ isConnected: false,
+ isInternetReachable: true,
type: NetInfoStateType.wifi,
})
renderNetInfoWrapper({
@@ -86,14 +83,14 @@ describe('NetInfoWrapper', () => {
onConnectionLost,
})
- expect(onInternetConnection).not.toHaveBeenCalled()
- expect(onInternetConnectionLost).toHaveBeenCalledTimes(1)
+ expect(mockEventBus.emit).toHaveBeenCalledWith(NETWORK_EVENTS.OFFLINE)
+ expect(analytics.logConnectionInfo).toHaveBeenCalledWith({ type: 'wifi' })
})
- it('should log network information when wifi is used', async () => {
+ it('should emit no event on event bus if network info is null', () => {
mockedUseNetInfo.mockReturnValueOnce({
- isConnected: false,
- isInternetReachable: true,
+ isConnected: null,
+ isInternetReachable: null,
type: NetInfoStateType.wifi,
})
renderNetInfoWrapper({
@@ -103,7 +100,7 @@ describe('NetInfoWrapper', () => {
onConnectionLost,
})
- expect(analytics.logConnectionInfo).toHaveBeenCalledWith({ type: 'wifi' })
+ expect(mockEventBus.emit).not.toHaveBeenCalled()
})
it('should log network information when cellular is used', async () => {
@@ -144,24 +141,7 @@ describe('NetInfoWrapper', () => {
expect(analytics.logConnectionInfo).not.toHaveBeenCalled()
})
- it('should display children if splashscreen is not visible and there is no network', () => {
- mockedUseNetInfo.mockReturnValueOnce({
- isConnected: false,
- isInternetReachable: false,
- type: NetInfoStateType.unknown,
- })
- renderNetInfoWrapper({
- onInternetConnection,
- onInternetConnectionLost,
- onConnection,
- onConnectionLost,
- })
-
- expect(screen.getByTestId('dumbComponent')).toBeOnTheScreen()
- })
-
- it('should not display children if splashscreen is visible and there is no network', () => {
- mockUseSplashScreenContext.mockReturnValueOnce({ isSplashScreenHidden: false })
+ it('should not display children if there is no network', () => {
mockedUseNetInfo.mockReturnValueOnce({
isConnected: false,
isInternetReachable: false,
diff --git a/src/libs/network/NetInfoWrapper.tsx b/src/libs/network/NetInfoWrapper.tsx
index ace14976ef1..1b33ba1dd04 100644
--- a/src/libs/network/NetInfoWrapper.tsx
+++ b/src/libs/network/NetInfoWrapper.tsx
@@ -2,12 +2,12 @@
import { NetInfoState, NetInfoStateType } from '@react-native-community/netinfo'
import React, { createContext, memo, useContext, useEffect } from 'react'
import { Platform } from 'react-native'
-import { onlineManager } from 'react-query'
+import { NETWORK_EVENTS } from 'events/eventNames'
+import { useEventBus } from 'events/useEventBus'
import { analytics } from 'libs/analytics'
+import { OfflinePage } from 'libs/network/OfflinePage'
import { useNetInfo } from 'libs/network/useNetInfo'
-import { useSplashScreenContext } from 'libs/splashscreen'
-import { SNACK_BAR_TIME_OUT, useSnackBarContext } from 'ui/components/snackBar/SnackBarContext'
export const NetInfoWrapper = memo(function NetInfoWrapper({
children,
@@ -15,20 +15,17 @@ export const NetInfoWrapper = memo(function NetInfoWrapper({
children: React.JSX.Element
}) {
const networkInfo = useNetInfo()
- const { showInfoSnackBar } = useSnackBarContext()
- const isConnected = !!networkInfo.isConnected && !!networkInfo.isInternetReachable
- const { isSplashScreenHidden } = useSplashScreenContext()
+ const eventBus = useEventBus()
+ const isConnected = networkInfo.isConnected && networkInfo.isInternetReachable
useEffect(() => {
- onlineManager.setOnline(isConnected)
- if (isConnected === false) {
- showInfoSnackBar({
- message: 'Aucune connexion internet. RĂ©essaie plus tard',
- timeout: SNACK_BAR_TIME_OUT,
- })
+ if (isConnected === null) {
+ return
}
+ eventBus.emit(isConnected ? NETWORK_EVENTS.ONLINE : NETWORK_EVENTS.OFFLINE)
+ // eventBus is not necessary in dependency array
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [networkInfo.isConnected, networkInfo.isInternetReachable])
+ }, [isConnected])
useEffect(() => {
const connectionType = networkInfo.type
@@ -47,7 +44,7 @@ export const NetInfoWrapper = memo(function NetInfoWrapper({
return (
- {!isSplashScreenHidden && !isConnected ? null : children}
+ {isConnected ? children : }
)
})
diff --git a/src/libs/splashscreen/splashscreen.native.test.tsx b/src/libs/splashscreen/splashscreen.native.test.tsx
index 1ef1ec04619..276cf246586 100644
--- a/src/libs/splashscreen/splashscreen.native.test.tsx
+++ b/src/libs/splashscreen/splashscreen.native.test.tsx
@@ -1,56 +1,30 @@
-import mockdate from 'mockdate'
+import React, { PropsWithChildren } from 'react'
+import { eventBus } from 'events/eventBus'
+import { EventBusProvider } from 'events/EventBusProvider'
+import { SPLASHSCREEN_EVENTS } from 'events/eventNames'
import { SplashScreenProvider, useSplashScreenContext } from 'libs/splashscreen/splashscreen'
import { renderHook, act } from 'tests/utils'
-const MIN_SPLASHSCREEN_DURATION_IN_MS = 2000
-
-const TODAY = new Date('2022-10-14')
-
-jest.useFakeTimers()
-
describe('useSplashScreenContext()', () => {
- beforeEach(() => {
- mockdate.set(TODAY)
- jest.clearAllTimers()
- })
-
- it('should hide splashscreen without waiting when it has been shown for long enough', () => {
- const { result } = renderSplashScreenHook()
- mockdate.set(TODAY.getTime() + MIN_SPLASHSCREEN_DURATION_IN_MS)
-
- act(() => {
- result.current.hideSplashScreen?.()
- })
-
- expect(result.current.isSplashScreenHidden).toBe(true)
- })
-
- it('should not hide splashscreen when it has not been shown for long enough', () => {
+ it('should hide splashscreen when "splashscreen.hide" event is emitted', async () => {
const { result } = renderSplashScreenHook()
-
act(() => {
- result.current.hideSplashScreen?.()
- jest.advanceTimersByTime(MIN_SPLASHSCREEN_DURATION_IN_MS - 1)
- })
-
- expect(result.current.isSplashScreenHidden).toBe(false)
- })
-
- it('should hide splashscreen when it has been shown for long enough', () => {
- const { result } = renderSplashScreenHook()
-
- act(() => {
- result.current.hideSplashScreen?.()
- jest.advanceTimersByTime(MIN_SPLASHSCREEN_DURATION_IN_MS)
+ eventBus.emit(SPLASHSCREEN_EVENTS.HIDE)
})
expect(result.current.isSplashScreenHidden).toBe(true)
})
})
+const Wrapper = ({ children }: PropsWithChildren) => (
+
+ {children}
+
+)
+
function renderSplashScreenHook() {
return renderHook(useSplashScreenContext, {
- wrapper: SplashScreenProvider,
+ wrapper: Wrapper,
})
}
diff --git a/src/libs/splashscreen/splashscreen.tsx b/src/libs/splashscreen/splashscreen.tsx
index 08e60fe053b..aa7cc77a454 100644
--- a/src/libs/splashscreen/splashscreen.tsx
+++ b/src/libs/splashscreen/splashscreen.tsx
@@ -1,9 +1,18 @@
-import React, { createContext, useCallback, useContext, memo, useState, useMemo } from 'react'
+import React, {
+ createContext,
+ useContext,
+ memo,
+ useState,
+ useMemo,
+ useEffect,
+ useCallback,
+} from 'react'
import SplashScreen from 'react-native-lottie-splash-screen'
-import { SplashScreenContextInterface } from './types'
+import { SPLASHSCREEN_EVENTS } from 'events/eventNames'
+import { useEventBus } from 'events/useEventBus'
-const MIN_SPLASHSCREEN_DURATION_IN_MS = 2000
+import { SplashScreenContextInterface } from './types'
const SplashScreenContext = createContext({
isSplashScreenHidden: false,
@@ -16,29 +25,20 @@ export function useSplashScreenContext() {
export const SplashScreenProvider = memo(function SplashScreenProvider(props: {
children: React.ReactNode
}) {
- const splashScreenBeginningTime = new Date().getTime()
const [isSplashScreenHidden, setIsSplashScreenHidden] = useState(false)
+ const eventBus = useEventBus()
- const hideSplashscreenCallback = useCallback(() => {
+ const hideSplashScreen = useCallback(() => {
SplashScreen.hide()
setIsSplashScreenHidden(true)
}, [])
- const hideSplashScreen = useCallback(() => {
- const splashScreenDisplayDuration = new Date().getTime() - splashScreenBeginningTime
- if (splashScreenDisplayDuration < MIN_SPLASHSCREEN_DURATION_IN_MS) {
- setTimeout(
- hideSplashscreenCallback,
- MIN_SPLASHSCREEN_DURATION_IN_MS - splashScreenDisplayDuration
- )
- } else {
- hideSplashscreenCallback()
- }
- }, [splashScreenBeginningTime, hideSplashscreenCallback])
-
- const value = useMemo(
- () => ({ isSplashScreenHidden, hideSplashScreen }),
- [hideSplashScreen, isSplashScreenHidden]
- )
+ useEffect(() => {
+ eventBus.once(SPLASHSCREEN_EVENTS.HIDE, hideSplashScreen)
+ // eventBus is not necessary in dependency array
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ const value = useMemo(() => ({ isSplashScreenHidden }), [isSplashScreenHidden])
return {props.children}
})
diff --git a/src/libs/splashscreen/splashscreen.web.tsx b/src/libs/splashscreen/splashscreen.web.tsx
index 85d31f13029..b369d570a9e 100644
--- a/src/libs/splashscreen/splashscreen.web.tsx
+++ b/src/libs/splashscreen/splashscreen.web.tsx
@@ -8,7 +8,7 @@ import { SplashScreenContextInterface } from './types'
*/
export function useSplashScreenContext(): SplashScreenContextInterface {
- return { isSplashScreenHidden: true, hideSplashScreen: undefined }
+ return { isSplashScreenHidden: true }
}
export const SplashScreenProvider: React.FC<{ children: React.ReactElement }> = (props) =>
diff --git a/src/libs/splashscreen/types.ts b/src/libs/splashscreen/types.ts
index ccce7de888b..a5cdc232d39 100644
--- a/src/libs/splashscreen/types.ts
+++ b/src/libs/splashscreen/types.ts
@@ -1,4 +1,3 @@
export interface SplashScreenContextInterface {
isSplashScreenHidden: boolean
- hideSplashScreen?: () => void
}
diff --git a/src/ui/components/snackBar/SnackBarContext.tsx b/src/ui/components/snackBar/SnackBarContext.tsx
index 02f89a28e97..198f25a74c3 100644
--- a/src/ui/components/snackBar/SnackBarContext.tsx
+++ b/src/ui/components/snackBar/SnackBarContext.tsx
@@ -1,6 +1,10 @@
-import React, { createContext, memo, useContext, useRef, useState } from 'react'
+import React, { createContext, memo, useContext, useEffect, useRef, useState } from 'react'
import { useTheme } from 'styled-components/native'
+import { SNACKBAR_EVENTS } from 'events/eventNames'
+import { SnackBarEventPayload } from 'events/types'
+import { useEventBus } from 'events/useEventBus'
+
import { mapSnackBarTypeToStyle } from './mapSnackBarTypeToStyle'
import { SnackBar, SnackBarProps } from './SnackBar'
import { SnackBarHelperSettings, SnackBarSettings, SnackBarType } from './types'
@@ -29,6 +33,7 @@ export const SnackBarProvider = memo(function SnackBarProviderComponent({
children: React.ReactNode
}) {
const theme = useTheme()
+ const eventBus = useEventBus()
const [snackBarProps, setSnackBarProps] = useState({
visible: false,
message: '',
@@ -66,6 +71,28 @@ export const SnackBarProvider = memo(function SnackBarProviderComponent({
}),
})
+ const handleShowSnackBarEvent = ({ message, type }: SnackBarEventPayload) => {
+ const { showErrorSnackBar, showInfoSnackBar, showSuccessSnackBar } = snackBarToolsRef.current
+ const showMessageFnMap = {
+ error: showErrorSnackBar,
+ info: showInfoSnackBar,
+ success: showSuccessSnackBar,
+ }
+
+ showMessageFnMap[type]?.({ message, timeout: SNACK_BAR_TIME_OUT })
+ }
+
+ useEffect(() => {
+ eventBus.on(SNACKBAR_EVENTS.SHOW, handleShowSnackBarEvent)
+ eventBus.on(SNACKBAR_EVENTS.HIDE, hideSnackBar)
+ return () => {
+ eventBus.off(SNACKBAR_EVENTS.SHOW, handleShowSnackBarEvent)
+ eventBus.off(SNACKBAR_EVENTS.HIDE, hideSnackBar)
+ }
+ // eventBus is not necessary in dependency array
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
return (