Skip to content

Commit

Permalink
Move tools to notifications package
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeljscript committed Oct 2, 2024
1 parent b8904d1 commit 8d2cbb4
Show file tree
Hide file tree
Showing 19 changed files with 446 additions and 178 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,48 @@
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'
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 (
<NotificationProvider manager={manager}>
<Screen />
</NotificationProvider>
)
}

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 (
<SafeAreaView edges={['bottom', 'top', 'left', 'right']}>
<ScrollableView>
<ScrollView>
<View style={{padding: 16, gap: 8}}>
<Text style={{fontSize: 24}}>Notifications Playground</Text>

Expand All @@ -48,7 +58,7 @@ export const NotificationsDevScreen = () => {

<ReceivedNotificationsList />
</View>
</ScrollableView>
</ScrollView>
</SafeAreaView>
)
}
Expand Down
159 changes: 20 additions & 139 deletions apps/wallet-mobile/src/features/Notifications/useCases/common/hooks.ts
Original file line number Diff line number Diff line change
@@ -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<NotificationTypes.Config, Error> = {}) => {
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<ReadonlyArray<NotificationTypes.Event>, 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}`)
})
}

Expand Down Expand Up @@ -112,34 +50,11 @@ export const useNotificationsManager = (options?: {
return manager
}

export const useMockedNotifications = () => {
const {send} = useSendNotification()
const [transactionReceivedSubject] = React.useState(new Subject<NotificationTypes.TransactionReceivedEvent>())
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({
Expand All @@ -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<NotificationTypes.TransactionReceivedEvent>())

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])
}
Original file line number Diff line number Diff line change
@@ -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<NotificationTypes.TransactionReceivedEvent>())
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}
}
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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}
}
Original file line number Diff line number Diff line change
@@ -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)) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
})
}
}
Expand All @@ -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<NotificationTypes.TransactionReceivedEvent>())

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
}
Loading

0 comments on commit 8d2cbb4

Please sign in to comment.