about summary refs log tree commit diff
path: root/src/state/queries
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-07-24 20:09:20 +0100
committerGitHub <noreply@github.com>2024-07-24 20:09:20 +0100
commitcfb8a3160e0092990bafd05cb97006720400448a (patch)
tree0e7b6fe5bc4e3e3d9ee25e228de228d24b78de40 /src/state/queries
parent9bd8393685cb6f2640dd33ee5707f3cb710f1365 (diff)
downloadvoidsky-cfb8a3160e0092990bafd05cb97006720400448a.tar.zst
Priority notifications (#4798)
* new settings screen

* bring back the spinner

* add experimental language

* fix typo, change leading

* integrate priority notifications API

* update package

* use refetch instead of invalidateQueries

* fix read-after-write issue by polling for update

* add spinner for initial load

* rm onmutate, it's overcomplicated

* set error state eagerly

* Change language in description

Co-authored-by: Hailey <me@haileyok.com>

* prettier

* add `Toggle.Platform`

* extract out mutation hook + error state

* rm useless cache mutation

* disambiguate isError and isPending

* rm unused isError

---------

Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>
Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/state/queries')
-rw-r--r--src/state/queries/notifications/feed.ts37
-rw-r--r--src/state/queries/notifications/settings.ts67
-rw-r--r--src/state/queries/notifications/types.ts1
-rw-r--r--src/state/queries/notifications/util.ts8
4 files changed, 97 insertions, 16 deletions
diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts
index 17ee90929..3cafcb716 100644
--- a/src/state/queries/notifications/feed.ts
+++ b/src/state/queries/notifications/feed.ts
@@ -46,11 +46,14 @@ const PAGE_SIZE = 30
 type RQPageParam = string | undefined
 
 const RQKEY_ROOT = 'notification-feed'
-export function RQKEY() {
-  return [RQKEY_ROOT]
+export function RQKEY(priority?: false) {
+  return [RQKEY_ROOT, priority]
 }
 
-export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
+export function useNotificationFeedQuery(opts?: {
+  enabled?: boolean
+  overridePriorityNotifications?: boolean
+}) {
   const agent = useAgent()
   const queryClient = useQueryClient()
   const moderationOpts = useModerationOpts()
@@ -59,6 +62,10 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
   const lastPageCountRef = useRef(0)
   const gate = useGate()
 
+  // false: force showing all notifications
+  // undefined: let the server decide
+  const priority = opts?.overridePriorityNotifications ? false : undefined
+
   const query = useInfiniteQuery<
     FeedPage,
     Error,
@@ -67,7 +74,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
     RQPageParam
   >({
     staleTime: STALE.INFINITY,
-    queryKey: RQKEY(),
+    queryKey: RQKEY(priority),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       let page
       if (!pageParam) {
@@ -75,17 +82,17 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
         page = unreads.getCachedUnreadPage()
       }
       if (!page) {
-        page = (
-          await fetchPage({
-            agent,
-            limit: PAGE_SIZE,
-            cursor: pageParam,
-            queryClient,
-            moderationOpts,
-            fetchAdditionalData: true,
-            shouldUngroupFollowBacks: () => gate('ungroup_follow_backs'),
-          })
-        ).page
+        const {page: fetchedPage} = await fetchPage({
+          agent,
+          limit: PAGE_SIZE,
+          cursor: pageParam,
+          queryClient,
+          moderationOpts,
+          fetchAdditionalData: true,
+          shouldUngroupFollowBacks: () => gate('ungroup_follow_backs'),
+          priority,
+        })
+        page = fetchedPage
       }
 
       // if the first page has an unread, mark all read
diff --git a/src/state/queries/notifications/settings.ts b/src/state/queries/notifications/settings.ts
new file mode 100644
index 000000000..78ecbd9f7
--- /dev/null
+++ b/src/state/queries/notifications/settings.ts
@@ -0,0 +1,67 @@
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useMutation, useQueryClient} from '@tanstack/react-query'
+
+import {until} from '#/lib/async/until'
+import {logger} from '#/logger'
+import {RQKEY as RQKEY_NOTIFS} from '#/state/queries/notifications/feed'
+import {useAgent} from '#/state/session'
+import * as Toast from '#/view/com/util/Toast'
+
+export function useNotificationsSettingsMutation() {
+  const {_} = useLingui()
+  const agent = useAgent()
+  const queryClient = useQueryClient()
+
+  return useMutation({
+    mutationFn: async (keys: string[]) => {
+      const enabled = keys[0] === 'enabled'
+
+      await agent.api.app.bsky.notification.putPreferences({
+        priority: enabled,
+      })
+
+      await until(
+        5, // 5 tries
+        1e3, // 1s delay between tries
+        res => res.data.priority === enabled,
+        () => agent.api.app.bsky.notification.listNotifications({limit: 1}),
+      )
+
+      eagerlySetCachedPriority(queryClient, enabled)
+    },
+    onError: err => {
+      logger.error('Failed to save notification preferences', {
+        safeMessage: err,
+      })
+      Toast.show(
+        _(msg`Failed to save notification preferences, please try again`),
+        'xmark',
+      )
+    },
+    onSuccess: () => {
+      Toast.show(_(msg`Preference saved`))
+    },
+    onSettled: () => {
+      queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS()})
+    },
+  })
+}
+
+function eagerlySetCachedPriority(
+  queryClient: ReturnType<typeof useQueryClient>,
+  enabled: boolean,
+) {
+  queryClient.setQueryData(RQKEY_NOTIFS(), (old: any) => {
+    if (!old) return old
+    return {
+      ...old,
+      pages: old.pages.map((page: any) => {
+        return {
+          ...page,
+          priority: enabled,
+        }
+      }),
+    }
+  })
+}
diff --git a/src/state/queries/notifications/types.ts b/src/state/queries/notifications/types.ts
index d40a07b12..c96374eb8 100644
--- a/src/state/queries/notifications/types.ts
+++ b/src/state/queries/notifications/types.ts
@@ -22,6 +22,7 @@ export interface FeedPage {
   cursor: string | undefined
   seenAt: Date
   items: FeedNotification[]
+  priority: boolean
 }
 
 export interface CachedFeedPage {
diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts
index 2f2c242d8..7651e414a 100644
--- a/src/state/queries/notifications/util.ts
+++ b/src/state/queries/notifications/util.ts
@@ -39,10 +39,15 @@ export async function fetchPage({
   moderationOpts: ModerationOpts | undefined
   fetchAdditionalData: boolean
   shouldUngroupFollowBacks?: () => boolean
-}): Promise<{page: FeedPage; indexedAt: string | undefined}> {
+  priority?: boolean
+}): Promise<{
+  page: FeedPage
+  indexedAt: string | undefined
+}> {
   const res = await agent.listNotifications({
     limit,
     cursor,
+    // priority,
   })
 
   const indexedAt = res.data.notifications[0]?.indexedAt
@@ -88,6 +93,7 @@ export async function fetchPage({
       cursor: res.data.cursor,
       seenAt,
       items: notifsGrouped,
+      priority: res.data.priority ?? false,
     },
     indexedAt,
   }