Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(notifications): Centralize notification state update logic #606

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 48 additions & 39 deletions src/reducers/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NotifyClientTypes } from '@walletconnect/notify-client'

// Notification state for each topic
export interface TopicNotificationsState {
fullNotifications: NotifyClientTypes.NotifyNotification[]
existingIds: Set<string>
Expand Down Expand Up @@ -28,80 +29,88 @@ export type NotificationsActions =
topic: string
}

// Opted for a reducer since the state changes are complex enough to warrant
// changes to a set and an array. Having all that inside the hooks would
// cause too many updates to the hooks, causing unnecessary rerenders.
export const notificationsReducer = (
state: NotificationsState,
action: NotificationsActions
): NotificationsState => {
const topicState = state[action.topic] as TopicNotificationsState | undefined

function getTopicState(notifications: NotifyClientTypes.NotifyNotification[]) {
const ids = topicState?.existingIds || new Set<string>()
const filteredNotifications = notifications.filter(val => !ids.has(val.id))
const notificationIds = notifications.map(notification => notification.id)
// Updates the topic state, filters notifications
function getUpdatedTopicState(
topicState: TopicNotificationsState | undefined,
notifications: NotifyClientTypes.NotifyNotification[]
) {
const existingIds = topicState?.existingIds || new Set<string>() // Set of IDs for already existing notifications
const filteredNotifications = notifications.filter(notification => !existingIds.has(notification.id)) // Filters out new notifications

const fullNotifications = topicState?.fullNotifications || []
const newFullIdsSet = new Set(topicState?.existingIds || [])
const fullNotifications = topicState?.fullNotifications || []
const updatedIds = new Set(existingIds)

for (const val of notificationIds) {
newFullIdsSet.add(val)
}
notifications.forEach(notification => updatedIds.add(notification.id))

return {
filteredNotifications,
fullNotifications,
newFullIdsSet
}
return {
filteredNotifications,
fullNotifications,
updatedIds
}
}

// Reducer for handling notification actions
export const notificationsReducer = (
state: NotificationsState,
action: NotificationsActions
): NotificationsState => {
const topicState = state[action.topic]

switch (action.type) {
case 'FETCH_NOTIFICATIONS_LOADING': {
if (topicState) {
return {
...state,
[action.topic]: {
...topicState,
isLoading: true
// Sets loading flag for the topic
return topicState
? {
...state,
[action.topic]: {
...topicState,
isLoading: true
}
}
}
}
return state
: state
}

case 'UNSHIFT_NEW_NOTIFICATIONS': {
const { filteredNotifications, fullNotifications, newFullIdsSet } = getTopicState(
// Adds new notifications to the beginning of the list
const { filteredNotifications, fullNotifications, updatedIds } = getUpdatedTopicState(
topicState,
action.notifications
)
const unshiftedNotifications = filteredNotifications.concat(fullNotifications)
const unshiftedNotifications = [...filteredNotifications, ...fullNotifications]

return {
...state,
[action.topic]: {
...topicState,
existingIds: newFullIdsSet,
existingIds: updatedIds,
fullNotifications: unshiftedNotifications,
hasMore: topicState?.hasMore || false,
isLoading: false
}
}
}

case 'FETCH_NOTIFICATIONS_DONE': {
const { filteredNotifications, fullNotifications, newFullIdsSet } = getTopicState(
// Completes the loading of notifications
const { filteredNotifications, fullNotifications, updatedIds } = getUpdatedTopicState(
topicState,
action.notifications
)
const concatenatedNotification = fullNotifications.concat(filteredNotifications)
const concatenatedNotifications = [...fullNotifications, ...filteredNotifications]

return {
...state,
[action.topic]: {
...topicState,
existingIds: newFullIdsSet,
fullNotifications: concatenatedNotification,
existingIds: updatedIds,
fullNotifications: concatenatedNotifications,
hasMore: action.hasMore,
isLoading: false
}
}
}

default:
return state
}
}