diff --git a/apps/wallet-mobile/src/features/Notifications/useCases/NotificationsDevScreen.tsx b/apps/wallet-mobile/src/features/Notifications/useCases/NotificationsDevScreen.tsx
index 1f80d5cd1c..004b94f495 100644
--- a/apps/wallet-mobile/src/features/Notifications/useCases/NotificationsDevScreen.tsx
+++ b/apps/wallet-mobile/src/features/Notifications/useCases/NotificationsDevScreen.tsx
@@ -1,3 +1,11 @@
+import {
+ NotificationProvider,
+ useNotificationManager,
+ useNotificationsConfig,
+ useReceivedNotificationEvents,
+ useResetNotificationsConfig,
+ useUpdateNotificationsConfig,
+} from '@yoroi/notifications'
import {useTheme} from '@yoroi/theme'
import {Notifications as NotificationTypes} from '@yoroi/types'
import * as React from 'react'
@@ -5,34 +13,36 @@ import {StyleSheet, Switch as RNSwitch, Text, View} from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context'
import {Button} from '../../../components/Button/Button'
-import {ScrollableView} from '../../../components/ScrollableView'
-import {
- useHandleNotification,
- useMockedNotifications,
- useNotificationsConfig,
- useReceivedNotificationEvents,
- useRequestPermissions,
- useResetNotificationsConfig,
- useUpdateNotificationsConfig,
-} from './common/hooks'
+import {ScrollView} from '../../../components/ScrollView/ScrollView'
+import {useMockedNotifications} from './common/mocks'
+import {createTransactionReceivedNotification} from './common/transaction-received-notification'
export const NotificationsDevScreen = () => {
- useRequestPermissions()
- useHandleNotification()
- const {triggerTransactionReceived} = useMockedNotifications()
+ const {manager} = useMockedNotifications()
+ return (
+
+
+
+ )
+}
+
+const Screen = () => {
+ const manager = useNotificationManager()
const handleOnTriggerTransactionReceived = () => {
- triggerTransactionReceived({
- previousTxsCounter: 0,
- nextTxsCounter: 1,
- txId: '123',
- isSentByUser: false,
- })
+ manager.notification$.next(
+ createTransactionReceivedNotification({
+ previousTxsCounter: 0,
+ nextTxsCounter: 1,
+ txId: '123',
+ isSentByUser: false,
+ }),
+ )
}
return (
-
+
Notifications Playground
@@ -48,7 +58,7 @@ export const NotificationsDevScreen = () => {
-
+
)
}
diff --git a/apps/wallet-mobile/src/features/Notifications/useCases/common/hooks.ts b/apps/wallet-mobile/src/features/Notifications/useCases/common/hooks.ts
index 3ee68b44de..31f8d522e1 100644
--- a/apps/wallet-mobile/src/features/Notifications/useCases/common/hooks.ts
+++ b/apps/wallet-mobile/src/features/Notifications/useCases/common/hooks.ts
@@ -1,87 +1,25 @@
import {Notification, Notifications} from '@jamsinclair/react-native-notifications'
-import {useAsyncStorage, useMutationWithInvalidations} from '@yoroi/common'
+import {useAsyncStorage} from '@yoroi/common'
import {notificationManagerMaker} from '@yoroi/notifications'
import {Notifications as NotificationTypes} from '@yoroi/types'
import * as React from 'react'
-import {UseMutationOptions, useQuery, UseQueryOptions} from 'react-query'
-import {Subject} from 'rxjs'
-import {useWalletManager} from '../../../WalletManager/context/WalletManagerProvider'
-import {
- checkForNewTransactions,
- createTransactionReceivedNotification,
- registerBackgroundFetchAsync,
- unregisterBackgroundFetchAsync,
-} from './transacrtion-received'
-import {useSendNotification} from './notifications'
+import {useEffect} from 'react'
-export const useRequestPermissions = () => {
- React.useEffect(() => {
- Notifications.registerRemoteNotifications()
- }, [])
-}
-
-export const useHandleNotification = () => {
- React.useEffect(() => {
- const s = Notifications.events().registerNotificationReceivedForeground(
- (notification: Notification, completion) => {
- console.log(`Notification received in foreground: ${notification.title} : ${notification.body}`)
- completion({alert: true, sound: true, badge: true})
- },
- )
-
- // TODO: Can we remove this?
- const s2 = Notifications.events().registerNotificationReceivedBackground((notification: Notification) => {
- console.log(`Notification received in background: ${notification.title} : ${notification.body}`)
- })
+import {displayNotificationEvent} from './notifications'
+import {useTransactionReceivedNotificationSubject} from './transaction-received-notification'
- return () => {
- s.remove()
- s2.remove()
- }
- }, [])
-}
-
-export const useNotificationsConfig = () => {
- const manager = useNotificationsManager()
- return useQuery(['notificationsConfig'], () => manager.config.read())
-}
+let initialized = false
-export const useUpdateNotificationsConfig = () => {
- const manager = useNotificationsManager()
-
- const mutationFn = async (newConfig: NotificationTypes.Config) => {
- await manager.config.save(newConfig)
- }
-
- return useMutationWithInvalidations({
- mutationFn,
- invalidateQueries: [['notificationsConfig']],
- })
-}
-
-export const useResetNotificationsConfig = (options: UseMutationOptions = {}) => {
- const manager = useNotificationsManager()
- const mutationFn = async () => {
- await manager.config.reset()
- return manager.config.read()
- }
-
- return useMutationWithInvalidations({
- mutationFn,
- invalidateQueries: [['notificationsConfig']],
- ...options,
+const init = () => {
+ if (initialized) return
+ initialized = true
+ Notifications.registerRemoteNotifications()
+ Notifications.events().registerNotificationReceivedForeground((_notification: Notification, completion) => {
+ completion({alert: true, sound: true, badge: true})
})
-}
-
-export const useReceivedNotificationEvents = (
- options: UseQueryOptions, Error> = {},
-) => {
- const manager = useNotificationsManager()
- const queryFn = () => manager.events.read()
- return useQuery({
- queryKey: ['receivedNotificationEvents'],
- queryFn,
- ...options,
+ // TODO: Can we remove this?
+ Notifications.events().registerNotificationReceivedBackground((notification: Notification) => {
+ console.log(`Notification received in background: ${notification.title} : ${notification.body}`)
})
}
@@ -112,34 +50,11 @@ export const useNotificationsManager = (options?: {
return manager
}
-export const useMockedNotifications = () => {
- const {send} = useSendNotification()
- const [transactionReceivedSubject] = React.useState(new Subject())
- const manager = useNotificationsManager({
- subscriptions: {[NotificationTypes.Trigger.TransactionReceived]: transactionReceivedSubject},
- })
- React.useEffect(() => {
- const subscription = manager.notification$.subscribe((notificationEvent) => {
- if (notificationEvent.trigger === NotificationTypes.Trigger.TransactionReceived) {
- send('Transaction received', 'You have received a new transaction')
- }
- })
- return () => {
- subscription.unsubscribe()
- }
- }, [manager, send])
-
- const triggerTransactionReceived = (metadata: NotificationTypes.TransactionReceivedEvent['metadata']) => {
- transactionReceivedSubject.next(createTransactionReceivedNotification(metadata))
- }
-
- return {triggerTransactionReceived}
-}
-
export const useNotifications = () => {
- useRequestPermissions()
- useHandleNotification()
- const {send} = useSendNotification()
+ useEffect(() => {
+ init()
+ }, [])
+
const transactionReceivedSubject = useTransactionReceivedNotificationSubject()
const manager = useNotificationsManager({
@@ -148,43 +63,9 @@ export const useNotifications = () => {
},
})
React.useEffect(() => {
- const subscription = manager.notification$.subscribe((notificationEvent) => {
- if (notificationEvent.trigger === NotificationTypes.Trigger.TransactionReceived) {
- console.log('Transaction received', notificationEvent.metadata)
- send('Transaction received', 'You have received a new transaction')
- }
- })
+ const subscription = manager.notification$.subscribe(displayNotificationEvent)
return () => {
subscription.unsubscribe()
}
- }, [manager, send])
-}
-
-const useTransactionReceivedNotificationSubject = () => {
- const {walletManager} = useWalletManager()
- const asyncStorage = useAsyncStorage()
- const [transactionReceivedSubject] = React.useState(new Subject())
-
- React.useEffect(() => {
- registerBackgroundFetchAsync()
- return () => {
- unregisterBackgroundFetchAsync()
- }
- }, [])
-
- React.useEffect(() => {
- const s1 = walletManager.syncWalletInfos$.subscribe(async (status) => {
- const walletInfos = Array.from(status.values())
- const walletsDoneSyncing = walletInfos.filter((info) => info.status === 'done')
- const areAllDone = walletsDoneSyncing.length === walletInfos.length
- if (!areAllDone) return
-
- await checkForNewTransactions(walletManager, asyncStorage)
- })
-
- return () => {
- s1.unsubscribe()
- }
- }, [walletManager, asyncStorage, transactionReceivedSubject])
- return transactionReceivedSubject
+ }, [manager])
}
diff --git a/apps/wallet-mobile/src/features/Notifications/useCases/common/mocks.ts b/apps/wallet-mobile/src/features/Notifications/useCases/common/mocks.ts
new file mode 100644
index 0000000000..6c94b7f026
--- /dev/null
+++ b/apps/wallet-mobile/src/features/Notifications/useCases/common/mocks.ts
@@ -0,0 +1,28 @@
+import {Notifications as NotificationTypes} from '@yoroi/types'
+import * as React from 'react'
+import {Subject} from 'rxjs'
+
+import {useNotificationsManager} from './hooks'
+import {displayNotificationEvent} from './notifications'
+import {createTransactionReceivedNotification} from './transaction-received-notification'
+
+export const useMockedNotifications = () => {
+ const [transactionReceivedSubject] = React.useState(new Subject())
+ const manager = useNotificationsManager({
+ subscriptions: {
+ [NotificationTypes.Trigger.TransactionReceived]: transactionReceivedSubject,
+ },
+ })
+ React.useEffect(() => {
+ const subscription = manager.notification$.subscribe(displayNotificationEvent)
+ return () => {
+ subscription.unsubscribe()
+ }
+ }, [manager])
+
+ const triggerTransactionReceived = (metadata: NotificationTypes.TransactionReceivedEvent['metadata']) => {
+ transactionReceivedSubject.next(createTransactionReceivedNotification(metadata))
+ }
+
+ return {triggerTransactionReceived, manager}
+}
diff --git a/apps/wallet-mobile/src/features/Notifications/useCases/common/notifications.ts b/apps/wallet-mobile/src/features/Notifications/useCases/common/notifications.ts
index 513a81b4df..4677d3581b 100644
--- a/apps/wallet-mobile/src/features/Notifications/useCases/common/notifications.ts
+++ b/apps/wallet-mobile/src/features/Notifications/useCases/common/notifications.ts
@@ -1,5 +1,12 @@
import {Notification, Notifications} from '@jamsinclair/react-native-notifications'
-import * as React from 'react'
+import {Notifications as NotificationTypes} from '@yoroi/types'
+
+export const displayNotificationEvent = (notificationEvent: NotificationTypes.Event) => {
+ if (notificationEvent.trigger === NotificationTypes.Trigger.TransactionReceived) {
+ console.log('Transaction received', notificationEvent.metadata)
+ sendNotification('Transaction received', 'You have received a new transaction')
+ }
+}
export const sendNotification = (title: string, body: string) => {
const notification = new Notification({
@@ -9,8 +16,3 @@ export const sendNotification = (title: string, body: string) => {
})
Notifications.postLocalNotification(notification.payload)
}
-
-export const useSendNotification = () => {
- const send = React.useCallback(sendNotification, [])
- return {send}
-}
diff --git a/apps/wallet-mobile/src/features/Notifications/useCases/common/transacrtion-received.ts b/apps/wallet-mobile/src/features/Notifications/useCases/common/transaction-received-notification.ts
similarity index 75%
rename from apps/wallet-mobile/src/features/Notifications/useCases/common/transacrtion-received.ts
rename to apps/wallet-mobile/src/features/Notifications/useCases/common/transaction-received-notification.ts
index 433338fea8..0289b028d8 100644
--- a/apps/wallet-mobile/src/features/Notifications/useCases/common/transacrtion-received.ts
+++ b/apps/wallet-mobile/src/features/Notifications/useCases/common/transaction-received-notification.ts
@@ -1,12 +1,17 @@
+import {useAsyncStorage} from '@yoroi/common'
+import {mountAsyncStorage} from '@yoroi/common/src'
+import {App, Notifications as NotificationTypes} from '@yoroi/types'
+import * as BackgroundFetch from 'expo-background-fetch'
import * as TaskManager from 'expo-task-manager'
+import * as React from 'react'
+import {Subject} from 'rxjs'
+import uuid from 'uuid'
+
+import {YoroiWallet} from '../../../../yoroi-wallets/cardano/types'
+import {useWalletManager} from '../../../WalletManager/context/WalletManagerProvider'
import {WalletManager, walletManager} from '../../../WalletManager/wallet-manager'
-import * as BackgroundFetch from 'expo-background-fetch'
-import {App, Notifications as NotificationTypes} from '@yoroi/types'
import {notificationManager} from './notification-manager'
-import {YoroiWallet} from '../../../../yoroi-wallets/cardano/types'
-import uuid from 'uuid'
-import {sendNotification} from './notifications'
-import {mountAsyncStorage} from '@yoroi/common/src'
+import {displayNotificationEvent} from './notifications'
const BACKGROUND_FETCH_TASK = 'yoroi-notifications-background-fetch'
if (!TaskManager.isTaskDefined(BACKGROUND_FETCH_TASK)) {
@@ -47,7 +52,7 @@ const syncAllWallets = async (walletManager: WalletManager) => {
const ids = [...walletManager.walletMetas.keys()]
const promises = ids.map((id) => {
const wallet = walletManager.getWalletById(id)
- if (!wallet) return
+ if (!wallet) return Promise.resolve()
return wallet.sync({isForced: true})
})
await Promise.all(promises)
@@ -85,7 +90,7 @@ export const checkForNewTransactions = async (walletManager: WalletManager, appS
}
const notification = createTransactionReceivedNotification(metadata)
notificationManager.events.save(notification)
- sendNotification('Transaction received from background', 'You have received a new transaction')
+ displayNotificationEvent(notification)
})
}
}
@@ -106,3 +111,32 @@ export const createTransactionReceivedNotification = (
metadata,
} as const
}
+
+export const useTransactionReceivedNotificationSubject = () => {
+ const {walletManager} = useWalletManager()
+ const asyncStorage = useAsyncStorage()
+ const [transactionReceivedSubject] = React.useState(new Subject())
+
+ React.useEffect(() => {
+ registerBackgroundFetchAsync()
+ return () => {
+ unregisterBackgroundFetchAsync()
+ }
+ }, [])
+
+ React.useEffect(() => {
+ const s1 = walletManager.syncWalletInfos$.subscribe(async (status) => {
+ const walletInfos = Array.from(status.values())
+ const walletsDoneSyncing = walletInfos.filter((info) => info.status === 'done')
+ const areAllDone = walletsDoneSyncing.length === walletInfos.length
+ if (!areAllDone) return
+
+ await checkForNewTransactions(walletManager, asyncStorage)
+ })
+
+ return () => {
+ s1.unsubscribe()
+ }
+ }, [walletManager, asyncStorage, transactionReceivedSubject])
+ return transactionReceivedSubject
+}
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/cardano-wallet.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/cardano-wallet.ts
index 1a2babeb60..e4bf088e85 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/cardano-wallet.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/cardano-wallet.ts
@@ -12,7 +12,7 @@ import {Buffer} from 'buffer'
import {freeze} from 'immer'
import _ from 'lodash'
import {defaultMemoize} from 'reselect'
-import {Observable, Subject} from 'rxjs'
+import {Observable} from 'rxjs'
import {buildPortfolioBalanceManager} from '../../features/Portfolio/common/helpers/build-balance-manager'
import {toBalanceManagerSyncArgs} from '../../features/Portfolio/common/transformers/toBalanceManagerSyncArgs'
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts
index 01e71b9202..d80576eb94 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts
@@ -12,7 +12,6 @@ import {
} from '@emurgo/yoroi-lib'
import {App, HW, Network, Portfolio, Wallet} from '@yoroi/types'
import {BigNumber} from 'bignumber.js'
-import {Subject} from 'rxjs'
import {WalletEncryptedStorage} from '../../kernel/storage/EncryptedStorage'
import type {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts b/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts
index d06dbba32d..92cb84b8f6 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts
@@ -7,14 +7,14 @@ import {createPrimaryTokenInfo} from '@yoroi/portfolio'
import {Balance, Portfolio, Wallet} from '@yoroi/types'
import BigNumber from 'bignumber.js'
import {noop} from 'lodash'
-import {Observable, Subject} from 'rxjs'
+import {Observable} from 'rxjs'
import {buildPortfolioTokenManagers} from '../../features/Portfolio/common/helpers/build-token-managers'
import {cardanoConfig} from '../../features/WalletManager/common/adapters/cardano/cardano-config'
import {buildNetworkManagers} from '../../features/WalletManager/network-manager/network-manager'
import {toTokenInfo, utf8ToHex} from '../cardano/api/utils'
import {CardanoTypes, YoroiWallet} from '../cardano/types'
-import {RawUtxo, TransactionInfo} from '../types/other'
+import {TransactionInfo} from '../types/other'
import {RemotePoolMetaSuccess, StakePoolInfosAndHistories, StakingInfo, StakingStatus} from '../types/staking'
import {YoroiNftModerationStatus, YoroiSignedTx, YoroiUnsignedTx} from '../types/yoroi'
import {getTokenFingerprint} from '../utils/format'
diff --git a/packages/notifications/src/index.ts b/packages/notifications/src/index.ts
index 41279074d4..f5a82a0b0b 100644
--- a/packages/notifications/src/index.ts
+++ b/packages/notifications/src/index.ts
@@ -1 +1,10 @@
export {notificationManagerMaker} from './notification-manager'
+export {useResetNotificationsConfig} from './translators/reactjs/useResetNotificationsConfig'
+export {
+ NotificationProvider,
+ useNotificationManager,
+} from './translators/reactjs/NotificationProvider'
+
+export {useNotificationsConfig} from './translators/reactjs/useNotificationsConfig'
+export {useReceivedNotificationEvents} from './translators/reactjs/useReceivedNotificationEvents'
+export {useUpdateNotificationsConfig} from './translators/reactjs/useUpdateNotificationsConfig'
diff --git a/packages/notifications/src/translators/reactjs/NotificationProvider.test.tsx b/packages/notifications/src/translators/reactjs/NotificationProvider.test.tsx
new file mode 100644
index 0000000000..f54cc74bb3
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/NotificationProvider.test.tsx
@@ -0,0 +1,48 @@
+import * as React from 'react'
+import {
+ NotificationProvider,
+ useNotificationManager,
+} from './NotificationProvider'
+import {notificationManagerMaker} from '../../notification-manager'
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {mountAsyncStorage} from '@yoroi/common'
+import {render, renderHook} from '@testing-library/react-native'
+
+describe('NotificationProvider', () => {
+ beforeEach(() => AsyncStorage.clear())
+
+ const eventsStorage = mountAsyncStorage({path: 'events/'})
+ const configStorage = mountAsyncStorage({path: 'config/'})
+
+ it('should render', () => {
+ const manager = notificationManagerMaker({
+ eventsStorage,
+ configStorage,
+ })
+
+ expect(
+ render(
+
+ <>>
+ ,
+ ),
+ ).toBeDefined()
+ })
+
+ it('should render hook without crashing', () => {
+ const manager = notificationManagerMaker({
+ eventsStorage,
+ configStorage,
+ })
+
+ const wrapper = ({children}: {children: React.ReactNode}) => (
+ {children}
+ )
+
+ expect(renderHook(() => useNotificationManager(), {wrapper})).toBeDefined()
+ })
+
+ it('should crash hook if it is not wrapped in NotificationProvider', () => {
+ expect(() => renderHook(() => useNotificationManager())).toThrow()
+ })
+})
diff --git a/packages/notifications/src/translators/reactjs/NotificationProvider.tsx b/packages/notifications/src/translators/reactjs/NotificationProvider.tsx
new file mode 100644
index 0000000000..be5d2670a2
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/NotificationProvider.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react'
+import {Notifications} from '@yoroi/types'
+
+type NotificationContextType = {
+ manager: Notifications.Manager
+}
+
+const Context = React.createContext(null)
+
+type Props = {
+ manager: Notifications.Manager
+ children: React.ReactNode
+}
+
+export const NotificationProvider = ({manager, children}: Props) => {
+ const value = React.useMemo(() => ({manager}), [manager])
+ return {children}
+}
+
+export const useNotificationManager = () => {
+ const context = React.useContext(Context)
+
+ if (context === null) {
+ throw new Error(
+ 'useNotificationManager must be used within a NotificationProvider',
+ )
+ }
+ return context.manager
+}
diff --git a/packages/notifications/src/translators/reactjs/useNotificationsConfig.test.tsx b/packages/notifications/src/translators/reactjs/useNotificationsConfig.test.tsx
new file mode 100644
index 0000000000..f86634e5a4
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useNotificationsConfig.test.tsx
@@ -0,0 +1,35 @@
+import * as React from 'react'
+import {renderHook, waitFor} from '@testing-library/react-native'
+import {useNotificationsConfig} from './useNotificationsConfig'
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {mountAsyncStorage, queryClientFixture} from '@yoroi/common'
+import {notificationManagerMaker} from '../../notification-manager'
+import {NotificationProvider} from './NotificationProvider'
+import {QueryClientProvider} from 'react-query'
+
+describe('useNotificationsConfig', () => {
+ beforeEach(() => AsyncStorage.clear())
+
+ const eventsStorage = mountAsyncStorage({path: 'events/'})
+ const configStorage = mountAsyncStorage({path: 'config/'})
+
+ it('should return notifications config', async () => {
+ const client = queryClientFixture()
+ const manager = notificationManagerMaker({
+ eventsStorage,
+ configStorage,
+ })
+
+ const wrapper = ({children}: {children: React.ReactNode}) => (
+
+
+ {children}
+
+
+ )
+ const {result} = renderHook(() => useNotificationsConfig(), {wrapper})
+ await waitFor(async () =>
+ expect(result.current.data).toEqual(await manager.config.read()),
+ )
+ })
+})
diff --git a/packages/notifications/src/translators/reactjs/useNotificationsConfig.ts b/packages/notifications/src/translators/reactjs/useNotificationsConfig.ts
new file mode 100644
index 0000000000..5164e6b5fc
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useNotificationsConfig.ts
@@ -0,0 +1,7 @@
+import {useQuery} from 'react-query'
+import {useNotificationManager} from './NotificationProvider'
+
+export const useNotificationsConfig = () => {
+ const manager = useNotificationManager()
+ return useQuery(['notificationsConfig'], () => manager.config.read())
+}
diff --git a/packages/notifications/src/translators/reactjs/useReceivedNotificationEvents.test.tsx b/packages/notifications/src/translators/reactjs/useReceivedNotificationEvents.test.tsx
new file mode 100644
index 0000000000..06ad3e1460
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useReceivedNotificationEvents.test.tsx
@@ -0,0 +1,37 @@
+import * as React from 'react'
+import {renderHook, waitFor} from '@testing-library/react-native'
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {mountAsyncStorage, queryClientFixture} from '@yoroi/common'
+import {notificationManagerMaker} from '../../notification-manager'
+import {NotificationProvider} from './NotificationProvider'
+import {QueryClientProvider} from 'react-query'
+import {useReceivedNotificationEvents} from './useReceivedNotificationEvents'
+
+describe('useReceivedNotificationEvents', () => {
+ beforeEach(() => AsyncStorage.clear())
+
+ const eventsStorage = mountAsyncStorage({path: 'events/'})
+ const configStorage = mountAsyncStorage({path: 'config/'})
+
+ it('should return notification events', async () => {
+ const client = queryClientFixture()
+ const manager = notificationManagerMaker({
+ eventsStorage,
+ configStorage,
+ })
+
+ const wrapper = ({children}: {children: React.ReactNode}) => (
+
+
+ {children}
+
+
+ )
+ const {result} = renderHook(() => useReceivedNotificationEvents(), {
+ wrapper,
+ })
+ await waitFor(async () =>
+ expect(result.current.data).toEqual(await manager.events.read()),
+ )
+ })
+})
diff --git a/packages/notifications/src/translators/reactjs/useReceivedNotificationEvents.ts b/packages/notifications/src/translators/reactjs/useReceivedNotificationEvents.ts
new file mode 100644
index 0000000000..53280041a5
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useReceivedNotificationEvents.ts
@@ -0,0 +1,15 @@
+import {useQuery, UseQueryOptions} from 'react-query'
+import {Notifications as NotificationTypes} from '@yoroi/types'
+import {useNotificationManager} from './NotificationProvider'
+
+export const useReceivedNotificationEvents = (
+ options: UseQueryOptions, Error> = {},
+) => {
+ const manager = useNotificationManager()
+ const queryFn = () => manager.events.read()
+ return useQuery({
+ queryKey: ['receivedNotificationEvents'],
+ queryFn,
+ ...options,
+ })
+}
diff --git a/packages/notifications/src/translators/reactjs/useResetNotificationsConfig.test.tsx b/packages/notifications/src/translators/reactjs/useResetNotificationsConfig.test.tsx
new file mode 100644
index 0000000000..df64748b80
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useResetNotificationsConfig.test.tsx
@@ -0,0 +1,48 @@
+import * as React from 'react'
+import {act, renderHook, waitFor} from '@testing-library/react-native'
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {mountAsyncStorage, queryClientFixture} from '@yoroi/common'
+import {notificationManagerMaker} from '../../notification-manager'
+import {NotificationProvider} from './NotificationProvider'
+import {QueryClientProvider} from 'react-query'
+import {useResetNotificationsConfig} from './useResetNotificationsConfig'
+import {Notifications} from '@yoroi/types'
+
+describe('useResetNotificationsConfig', () => {
+ beforeEach(() => AsyncStorage.clear())
+
+ const eventsStorage = mountAsyncStorage({path: 'events/'})
+ const configStorage = mountAsyncStorage({path: 'config/'})
+
+ it('should allow to reset config', async () => {
+ const client = queryClientFixture()
+ const manager = notificationManagerMaker({
+ eventsStorage,
+ configStorage,
+ })
+
+ const wrapper = ({children}: {children: React.ReactNode}) => (
+
+
+ {children}
+
+
+ )
+ const {result} = renderHook(() => useResetNotificationsConfig(), {
+ wrapper,
+ })
+ await manager.config.save({
+ ...(await manager.config.read()),
+ [Notifications.Trigger.TransactionReceived]: {
+ notify: false,
+ },
+ })
+ act(() => {
+ result.current.mutate()
+ })
+
+ await waitFor(async () =>
+ expect(result.current.data).toEqual(await manager.config.read()),
+ )
+ })
+})
diff --git a/packages/notifications/src/translators/reactjs/useResetNotificationsConfig.ts b/packages/notifications/src/translators/reactjs/useResetNotificationsConfig.ts
new file mode 100644
index 0000000000..18e57da40b
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useResetNotificationsConfig.ts
@@ -0,0 +1,20 @@
+import {UseMutationOptions} from 'react-query'
+import {Notifications as NotificationTypes} from '@yoroi/types'
+import {useMutationWithInvalidations} from '@yoroi/common'
+import {useNotificationManager} from './NotificationProvider'
+
+export const useResetNotificationsConfig = (
+ options: UseMutationOptions = {},
+) => {
+ const manager = useNotificationManager()
+ const mutationFn = async () => {
+ await manager.config.reset()
+ return manager.config.read()
+ }
+
+ return useMutationWithInvalidations({
+ mutationFn,
+ invalidateQueries: [['notificationsConfig']],
+ ...options,
+ })
+}
diff --git a/packages/notifications/src/translators/reactjs/useUpdateNotificationsConfig.test.tsx b/packages/notifications/src/translators/reactjs/useUpdateNotificationsConfig.test.tsx
new file mode 100644
index 0000000000..fbd9dd828d
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useUpdateNotificationsConfig.test.tsx
@@ -0,0 +1,50 @@
+import * as React from 'react'
+import {act, renderHook, waitFor} from '@testing-library/react-native'
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {mountAsyncStorage, queryClientFixture} from '@yoroi/common'
+import {notificationManagerMaker} from '../../notification-manager'
+import {NotificationProvider} from './NotificationProvider'
+import {QueryClientProvider} from 'react-query'
+import {Notifications} from '@yoroi/types'
+import {useUpdateNotificationsConfig} from './useUpdateNotificationsConfig'
+
+describe('useUpdateNotificationsConfig', () => {
+ beforeEach(() => AsyncStorage.clear())
+
+ const eventsStorage = mountAsyncStorage({path: 'events/'})
+ const configStorage = mountAsyncStorage({path: 'config/'})
+
+ it('should allow to update config', async () => {
+ const client = queryClientFixture()
+ const manager = notificationManagerMaker({
+ eventsStorage,
+ configStorage,
+ })
+
+ const wrapper = ({children}: {children: React.ReactNode}) => (
+
+
+ {children}
+
+
+ )
+ const {result} = renderHook(() => useUpdateNotificationsConfig(), {
+ wrapper,
+ })
+
+ const initialConfig = await manager.config.read()
+
+ await act(async () => {
+ result.current.mutate({
+ ...(await manager.config.read()),
+ [Notifications.Trigger.TransactionReceived]: {
+ notify: false,
+ },
+ })
+ })
+
+ await waitFor(async () =>
+ expect(await manager.config.read()).not.toEqual(initialConfig),
+ )
+ })
+})
diff --git a/packages/notifications/src/translators/reactjs/useUpdateNotificationsConfig.ts b/packages/notifications/src/translators/reactjs/useUpdateNotificationsConfig.ts
new file mode 100644
index 0000000000..83e5daabfd
--- /dev/null
+++ b/packages/notifications/src/translators/reactjs/useUpdateNotificationsConfig.ts
@@ -0,0 +1,16 @@
+import {Notifications as NotificationTypes} from '@yoroi/types'
+import {useMutationWithInvalidations} from '@yoroi/common'
+import {useNotificationManager} from './NotificationProvider'
+
+export const useUpdateNotificationsConfig = () => {
+ const manager = useNotificationManager()
+
+ const mutationFn = async (newConfig: NotificationTypes.Config) => {
+ await manager.config.save(newConfig)
+ }
+
+ return useMutationWithInvalidations({
+ mutationFn,
+ invalidateQueries: [['notificationsConfig']],
+ })
+}