about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-07-03 22:13:47 -0500
committerGitHub <noreply@github.com>2024-07-04 04:13:47 +0100
commit4f02da96c8c2483923fdf52d1ee7cd8f34b15fba (patch)
tree981775d5260a813079b5c2d1564380069440b193
parent0ed99b840d8de13465f010a6434dea50c72b3f62 (diff)
downloadvoidsky-4f02da96c8c2483923fdf52d1ee7cd8f34b15fba.tar.zst
[D1X] Pull out follow-backs for higher signal (#4719)
* Pull out follow-backs for higher signal

* Gate it

* Fix early gate check

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
-rw-r--r--src/lib/statsig/gates.ts5
-rw-r--r--src/state/queries/notifications/feed.ts3
-rw-r--r--src/state/queries/notifications/unread.tsx5
-rw-r--r--src/state/queries/notifications/util.ts23
-rw-r--r--src/view/com/notifications/FeedItem.tsx15
5 files changed, 41 insertions, 10 deletions
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index c8a55b928..6a4081185 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -2,11 +2,12 @@ export type Gate =
   // Keep this alphabetic please.
   | 'debug_show_feedcontext'
   | 'native_pwi_disabled'
+  | 'new_user_guided_tour'
+  | 'new_user_progress_guide'
   | 'onboarding_minimum_interests'
   | 'request_notifications_permission_after_onboarding_v2'
   | 'show_avi_follow_button'
   | 'show_follow_back_label_v2'
-  | 'new_user_guided_tour'
-  | 'new_user_progress_guide'
   | 'suggested_feeds_interstitial'
   | 'suggested_follows_interstitial'
+  | 'ungroup_follow_backs'
diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts
index 13ca3ffde..17ee90929 100644
--- a/src/state/queries/notifications/feed.ts
+++ b/src/state/queries/notifications/feed.ts
@@ -26,6 +26,7 @@ import {
   useQueryClient,
 } from '@tanstack/react-query'
 
+import {useGate} from '#/lib/statsig/statsig'
 import {useAgent} from '#/state/session'
 import {useModerationOpts} from '../../preferences/moderation-opts'
 import {STALE} from '..'
@@ -56,6 +57,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
   const unreads = useUnreadNotificationsApi()
   const enabled = opts?.enabled !== false
   const lastPageCountRef = useRef(0)
+  const gate = useGate()
 
   const query = useInfiniteQuery<
     FeedPage,
@@ -81,6 +83,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
             queryClient,
             moderationOpts,
             fetchAdditionalData: true,
+            shouldUngroupFollowBacks: () => gate('ungroup_follow_backs'),
           })
         ).page
       }
diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx
index 7bb325ea9..b5f7d0d60 100644
--- a/src/state/queries/notifications/unread.tsx
+++ b/src/state/queries/notifications/unread.tsx
@@ -8,6 +8,7 @@ import {useQueryClient} from '@tanstack/react-query'
 import EventEmitter from 'eventemitter3'
 
 import BroadcastChannel from '#/lib/broadcast'
+import {useGate} from '#/lib/statsig/statsig'
 import {logger} from '#/logger'
 import {useAgent, useSession} from '#/state/session'
 import {resetBadgeCount} from 'lib/notifications/notifications'
@@ -47,6 +48,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
   const agent = useAgent()
   const queryClient = useQueryClient()
   const moderationOpts = useModerationOpts()
+  const gate = useGate()
 
   const [numUnread, setNumUnread] = React.useState('')
 
@@ -149,6 +151,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
             // only fetch subjects when the page is going to be used
             // in the notifications query, otherwise skip it
             fetchAdditionalData: !!invalidate,
+            shouldUngroupFollowBacks: () => gate('ungroup_follow_backs'),
           })
           const unreadCount = countUnread(page)
           const unreadCountStr =
@@ -189,7 +192,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
         }
       },
     }
-  }, [setNumUnread, queryClient, moderationOpts, agent])
+  }, [setNumUnread, queryClient, moderationOpts, agent, gate])
   checkUnreadRef.current = api.checkUnread
 
   return (
diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts
index ade98b317..2f2c242d8 100644
--- a/src/state/queries/notifications/util.ts
+++ b/src/state/queries/notifications/util.ts
@@ -30,6 +30,7 @@ export async function fetchPage({
   queryClient,
   moderationOpts,
   fetchAdditionalData,
+  shouldUngroupFollowBacks,
 }: {
   agent: BskyAgent
   cursor: string | undefined
@@ -37,6 +38,7 @@ export async function fetchPage({
   queryClient: QueryClient
   moderationOpts: ModerationOpts | undefined
   fetchAdditionalData: boolean
+  shouldUngroupFollowBacks?: () => boolean
 }): Promise<{page: FeedPage; indexedAt: string | undefined}> {
   const res = await agent.listNotifications({
     limit,
@@ -51,7 +53,7 @@ export async function fetchPage({
   )
 
   // group notifications which are essentially similar (follows, likes on a post)
-  let notifsGrouped = groupNotifications(notifs)
+  let notifsGrouped = groupNotifications(notifs, {shouldUngroupFollowBacks})
 
   // we fetch subjects of notifications (usually posts) now instead of lazily
   // in the UI to avoid relayouts
@@ -109,6 +111,7 @@ export function shouldFilterNotif(
 
 export function groupNotifications(
   notifs: AppBskyNotificationListNotifications.Notification[],
+  options?: {shouldUngroupFollowBacks?: () => boolean},
 ): FeedNotification[] {
   const groupedNotifs: FeedNotification[] = []
   for (const notif of notifs) {
@@ -123,10 +126,20 @@ export function groupNotifications(
           notif.reasonSubject === groupedNotif.notification.reasonSubject &&
           notif.author.did !== groupedNotif.notification.author.did
         ) {
-          groupedNotif.additional = groupedNotif.additional || []
-          groupedNotif.additional.push(notif)
-          grouped = true
-          break
+          const nextIsFollowBack =
+            notif.reason === 'follow' && notif.author.viewer?.following
+          const prevIsFollowBack =
+            groupedNotif.notification.reason === 'follow' &&
+            groupedNotif.notification.author.viewer?.following
+          const shouldUngroup =
+            (nextIsFollowBack || prevIsFollowBack) &&
+            options?.shouldUngroupFollowBacks?.()
+          if (!shouldUngroup) {
+            groupedNotif.additional = groupedNotif.additional || []
+            groupedNotif.additional.push(notif)
+            grouped = true
+            break
+          }
         }
       }
     }
diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx
index 4f84385d2..1932efbd5 100644
--- a/src/view/com/notifications/FeedItem.tsx
+++ b/src/view/com/notifications/FeedItem.tsx
@@ -22,6 +22,7 @@ import {msg, plural, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useQueryClient} from '@tanstack/react-query'
 
+import {useGate} from '#/lib/statsig/statsig'
 import {FeedNotification} from '#/state/queries/notifications/feed'
 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -86,6 +87,7 @@ let FeedItem = ({
   const pal = usePalette('default')
   const {_} = useLingui()
   const t = useTheme()
+  const gate = useGate()
   const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
   const itemHref = useMemo(() => {
     if (item.type === 'post-like' || item.type === 'repost') {
@@ -168,6 +170,7 @@ let FeedItem = ({
     )
   }
 
+  let isFollowBack = false
   let action = ''
   let icon = (
     <HeartIconFilled
@@ -184,7 +187,15 @@ let FeedItem = ({
     action = _(msg`reposted your post`)
     icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} />
   } else if (item.type === 'follow') {
-    action = _(msg`followed you`)
+    if (
+      item.notification.author.viewer?.following &&
+      gate('ungroup_follow_backs')
+    ) {
+      isFollowBack = true
+      action = _(msg`followed you back`)
+    } else {
+      action = _(msg`followed you`)
+    }
     icon = <PersonPlusIcon size="xl" style={{color: t.palette.primary_500}} />
   } else if (item.type === 'feedgen-like') {
     action = _(msg`liked your custom feed`)
@@ -260,7 +271,7 @@ let FeedItem = ({
             visible={!isAuthorsExpanded}
             authors={authors}
             onToggleAuthorsExpanded={onToggleAuthorsExpanded}
-            showDmButton={item.type === 'starterpack-joined'}
+            showDmButton={item.type === 'starterpack-joined' || isFollowBack}
           />
           <ExpandedAuthorsList visible={isAuthorsExpanded} authors={authors} />
           <Text style={styles.meta}>