mirror of
https://github.com/oxynote/oxynote.git
synced 2026-10-17 02:49:18 +02:00
* overhaul the notif inbox * make hook summary abstract --------- Co-authored-by: David <SE.David@protonmail.com> Co-authored-by: david <58231312+davseby@users.noreply.github.com>
167 lines
4.5 KiB
TypeScript
167 lines
4.5 KiB
TypeScript
import type { EntryKey, UseInfiniteQueryData } from "@pinia/colada"
|
|
import isDeepEqual from "fast-deep-equal"
|
|
|
|
type NotificationPages = UseInfiniteQueryData<NotificationsResponse, number>
|
|
|
|
const NOTIFICATION_QUERY_KEYS = {
|
|
list: (limit: number) => ["notifications", "list", limit] as const,
|
|
count: (read: boolean) => ["notifications", "count", read] as const,
|
|
listRoot: ["notifications", "list"] as const,
|
|
countRoot: ["notifications", "count"] as const,
|
|
}
|
|
|
|
export default function () {
|
|
const { $coreAPIClient } = useNuxtApp()
|
|
const queryCache = useQueryCache()
|
|
|
|
function useFetchManyNotifications(
|
|
paramsRef: MaybeRefOrGetter<NotificationsParams>,
|
|
) {
|
|
return useInfiniteQuery({
|
|
key: () => NOTIFICATION_QUERY_KEYS.list(toValue(paramsRef).limit),
|
|
query: async ({ pageParam }) => {
|
|
const searchParams = new URLSearchParams({
|
|
limit: String(toValue(paramsRef).limit),
|
|
page: String(pageParam),
|
|
})
|
|
|
|
return await $coreAPIClient<NotificationsResponse>(
|
|
`/api/notifications?${searchParams.toString()}`,
|
|
{ method: "GET" },
|
|
)
|
|
},
|
|
initialPageParam: 1,
|
|
getNextPageParam: (lastPage, _pages, lastPageParam) =>
|
|
lastPageParam < lastPage.pageCount ? lastPageParam + 1 : null,
|
|
refetchOnMount: false,
|
|
refetchOnWindowFocus: false,
|
|
refetchOnReconnect: false,
|
|
staleTime: 60 * 1000, // 1 min
|
|
autoRefetch: true,
|
|
})
|
|
}
|
|
|
|
function useFetchNotificationCount(
|
|
paramsRef: MaybeRefOrGetter<NotificationsCountParams>,
|
|
) {
|
|
return useQuery({
|
|
key: () => {
|
|
const params = toValue(paramsRef)
|
|
|
|
return NOTIFICATION_QUERY_KEYS.count(params.read)
|
|
},
|
|
query: async () => {
|
|
const params = toValue(paramsRef)
|
|
const searchParams = new URLSearchParams()
|
|
|
|
searchParams.set("read", String(params.read))
|
|
|
|
const query = searchParams.toString()
|
|
const url = query
|
|
? `/api/notifications/count?${query}`
|
|
: `/api/notifications/count`
|
|
|
|
return await $coreAPIClient<{ count: number }>(url, { method: "GET" })
|
|
},
|
|
refetchOnMount: false,
|
|
refetchOnWindowFocus: false,
|
|
refetchOnReconnect: false,
|
|
staleTime: 60 * 1000, // 1 min
|
|
autoRefetch: true,
|
|
})
|
|
}
|
|
|
|
const markNotificationsRead = useMutation({
|
|
// count is hard to optimistically update, so we skip it
|
|
onMutate: (req) => {
|
|
const entries = queryCache.getEntries({
|
|
key: NOTIFICATION_QUERY_KEYS.listRoot,
|
|
})
|
|
// the key travels next to the data rather than inside it, so the
|
|
// bookkeeping never ends up in the cached data
|
|
const oldNotifs: { key: EntryKey; data: NotificationPages }[] = []
|
|
|
|
entries.forEach((entry) => {
|
|
const oldNotifData = clone(
|
|
queryCache.getQueryData<NotificationPages>(entry.key),
|
|
)
|
|
|
|
// entries can exist without data (a query that has not
|
|
// resolved yet); including them would make the update loop
|
|
// below throw on the missing pages array and abort the whole
|
|
// mutation
|
|
if (!oldNotifData) {
|
|
return
|
|
}
|
|
|
|
oldNotifs.push({ key: clone(entry.key), data: oldNotifData })
|
|
})
|
|
|
|
const newNotifs = clone(oldNotifs)
|
|
newNotifs.forEach(({ key, data }) => {
|
|
data.pages.forEach((page) => {
|
|
page.notifications.forEach((notif) => {
|
|
if (req.ids.length === 0 || req.ids.includes(notif.id)) {
|
|
notif.read = true
|
|
}
|
|
})
|
|
})
|
|
|
|
queryCache.setQueryData(key, data)
|
|
queryCache.cancelQueries({ key })
|
|
})
|
|
|
|
return { newNotifs, oldNotifs }
|
|
},
|
|
mutation: async (req: MarkNotificationsReadRequest) => {
|
|
await $coreAPIClient(`/api/notifications/read-status`, {
|
|
method: "PUT",
|
|
body: req,
|
|
})
|
|
},
|
|
async onSuccess() {
|
|
await queryCache.invalidateQueries({
|
|
key: NOTIFICATION_QUERY_KEYS.listRoot,
|
|
})
|
|
await queryCache.invalidateQueries({
|
|
key: NOTIFICATION_QUERY_KEYS.countRoot,
|
|
})
|
|
},
|
|
onError(_err, _data, { oldNotifs, newNotifs }) {
|
|
const entries = queryCache.getEntries({
|
|
key: NOTIFICATION_QUERY_KEYS.listRoot,
|
|
})
|
|
const cachedNotifs: { key: EntryKey; data: NotificationPages }[] = []
|
|
|
|
entries.forEach((entry) => {
|
|
const cachedNotifData = clone(
|
|
queryCache.getQueryData<NotificationPages>(entry.key),
|
|
)
|
|
|
|
// mirror the onMutate filter so the rollback comparison sees
|
|
// the same set of entries the optimistic update touched
|
|
if (!cachedNotifData) {
|
|
return
|
|
}
|
|
|
|
cachedNotifs.push({ key: clone(entry.key), data: cachedNotifData })
|
|
})
|
|
|
|
if (!isDeepEqual(newNotifs, cachedNotifs)) {
|
|
return
|
|
}
|
|
|
|
// rollback
|
|
oldNotifs?.forEach(({ key, data }) => {
|
|
queryCache.setQueryData(key, data)
|
|
})
|
|
},
|
|
})
|
|
|
|
return {
|
|
useFetchManyNotifications,
|
|
useFetchNotificationCount,
|
|
markNotificationsRead,
|
|
}
|
|
}
|