import { useMemo } from 'react' import { notification_subscribe_types, PrivateUser } from 'common/user' import { Notification } from 'common/notification' import { getNotificationsQuery } from 'web/lib/firebase/notifications' import { groupBy, map, partition } from 'lodash' import { useFirestoreQueryData } from '@react-query-firebase/firestore' export type NotificationGroup = { notifications: Notification[] groupedById: string isSeen: boolean timePeriod: string type: 'income' | 'normal' } function useNotifications(privateUser: PrivateUser) { const result = useFirestoreQueryData( ['notifications-all', privateUser.id], getNotificationsQuery(privateUser.id), { subscribe: true, includeMetadataChanges: true }, // Temporary workaround for react-query bug: // https://github.com/invertase/react-query-firebase/issues/25 { cacheTime: 0 } ) const notifications = useMemo(() => { if (!result.data) return undefined const notifications = result.data as Notification[] return getAppropriateNotifications( notifications, privateUser.notificationPreferences ).filter((n) => !n.isSeenOnHref) }, [privateUser.notificationPreferences, result.data]) return notifications } export function useUnseenNotifications(privateUser: PrivateUser) { const notifications = useNotifications(privateUser) return useMemo( () => notifications && notifications.filter((n) => !n.isSeen), [notifications] ) } export function useGroupedNotifications(privateUser: PrivateUser) { const notifications = useNotifications(privateUser) return useMemo(() => { if (notifications) return groupNotifications(notifications) }, [notifications]) } export function useUnseenGroupedNotification(privateUser: PrivateUser) { const notifications = useUnseenNotifications(privateUser) return useMemo(() => { if (notifications) return groupNotifications(notifications) }, [notifications]) } export function groupNotifications(notifications: Notification[]) { let notificationGroups: NotificationGroup[] = [] const notificationGroupsByDay = groupBy(notifications, (notification) => new Date(notification.createdTime).toDateString() ) const incomeSourceTypes = ['bonus', 'tip', 'loan', 'betting_streak_bonus'] Object.keys(notificationGroupsByDay).forEach((day) => { const notificationsGroupedByDay = notificationGroupsByDay[day] const [incomeNotifications, normalNotificationsGroupedByDay] = partition( notificationsGroupedByDay, (notification) => incomeSourceTypes.includes(notification.sourceType ?? '') ) if (incomeNotifications.length > 0) { notificationGroups = notificationGroups.concat({ notifications: incomeNotifications, groupedById: 'income' + day, isSeen: incomeNotifications[0].isSeen, timePeriod: day, type: 'income', }) } // Group notifications by contract, filtering out bonuses: const groupedNotificationsByContractId = groupBy( normalNotificationsGroupedByDay, (notification) => { return notification.sourceContractId } ) notificationGroups = notificationGroups.concat( map(groupedNotificationsByContractId, (notifications, contractId) => { const notificationsForContractId = groupedNotificationsByContractId[ contractId ].sort((a, b) => { return b.createdTime - a.createdTime }) // Create a notification group for each contract within each day const notificationGroup: NotificationGroup = { notifications: notificationsForContractId, groupedById: contractId, isSeen: notificationsForContractId[0].isSeen, timePeriod: day, type: 'normal', } return notificationGroup }) ) }) return notificationGroups } const lessPriorityReasons = [ 'on_contract_with_users_comment', 'on_contract_with_users_answer', // Notifications not currently generated for users who've sold their shares 'on_contract_with_users_shares_out', // Not sure if users will want to see these w/ less: // 'on_contract_with_users_shares_in', ] function getAppropriateNotifications( notifications: Notification[], notificationPreferences?: notification_subscribe_types ) { if (notificationPreferences === 'all') return notifications if (notificationPreferences === 'less') return notifications.filter( (n) => n.reason && // Show all contract notifications and any that aren't in the above list: (n.sourceType === 'contract' || !lessPriorityReasons.includes(n.reason)) ) if (notificationPreferences === 'none') return [] return notifications }