about summary refs log tree commit diff
path: root/src/view/com/posts
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/posts')
-rw-r--r--src/view/com/posts/Feed.tsx13
-rw-r--r--src/view/com/posts/FeedShutdownMsg.tsx159
2 files changed, 170 insertions, 2 deletions
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index c51733d1b..e45abfedc 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -14,6 +14,7 @@ import {useLingui} from '@lingui/react'
 import {useQueryClient} from '@tanstack/react-query'
 
 import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home'
+import {KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
 import {logEvent} from '#/lib/statsig/statsig'
 import {logger} from '#/logger'
 import {isWeb} from '#/platform/detection'
@@ -36,12 +37,14 @@ import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
 import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
 import {FeedErrorMessage} from './FeedErrorMessage'
+import {FeedShutdownMsg} from './FeedShutdownMsg'
 import {FeedSlice} from './FeedSlice'
 
 const LOADING_ITEM = {_reactKey: '__loading__'}
 const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
 const ERROR_ITEM = {_reactKey: '__error__'}
 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
+const FEED_SHUTDOWN_MSG_ITEM = {_reactKey: '__feed_shutdown_msg_item__'}
 
 // DISABLED need to check if this is causing random feed refreshes -prf
 // const REFRESH_AFTER = STALE.HOURS.ONE
@@ -96,7 +99,7 @@ let Feed = ({
   const [isPTRing, setIsPTRing] = React.useState(false)
   const checkForNewRef = React.useRef<(() => void) | null>(null)
   const lastFetchRef = React.useRef<number>(Date.now())
-  const feedType = feed.split('|')[0]
+  const [feedType, feedUri] = feed.split('|')
 
   const opts = React.useMemo(
     () => ({enabled, ignoreFilterFor}),
@@ -196,6 +199,9 @@ let Feed = ({
 
   const feedItems = React.useMemo(() => {
     let arr: any[] = []
+    if (KNOWN_SHUTDOWN_FEEDS.includes(feedUri)) {
+      arr = arr.concat([FEED_SHUTDOWN_MSG_ITEM])
+    }
     if (isFetched) {
       if (isError && isEmpty) {
         arr = arr.concat([ERROR_ITEM])
@@ -213,7 +219,7 @@ let Feed = ({
       arr.push(LOADING_ITEM)
     }
     return arr
-  }, [isFetched, isError, isEmpty, data])
+  }, [isFetched, isError, isEmpty, data, feedUri])
 
   // events
   // =
@@ -296,6 +302,8 @@ let Feed = ({
         )
       } else if (item === LOADING_ITEM) {
         return <PostFeedLoadingPlaceholder />
+      } else if (item === FEED_SHUTDOWN_MSG_ITEM) {
+        return <FeedShutdownMsg feedUri={feedUri} />
       } else if (item.rootUri === FALLBACK_MARKER_POST.post.uri) {
         // HACK
         // tell the user we fell back to discover
@@ -307,6 +315,7 @@ let Feed = ({
     },
     [
       feed,
+      feedUri,
       error,
       onPressTryAgain,
       onPressRetryLoadMore,
diff --git a/src/view/com/posts/FeedShutdownMsg.tsx b/src/view/com/posts/FeedShutdownMsg.tsx
new file mode 100644
index 000000000..bc047e831
--- /dev/null
+++ b/src/view/com/posts/FeedShutdownMsg.tsx
@@ -0,0 +1,159 @@
+import React from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {PROD_DEFAULT_FEED} from '#/lib/constants'
+import {logger} from '#/logger'
+import {
+  useAddSavedFeedsMutation,
+  usePreferencesQuery,
+  useRemoveFeedMutation,
+  useUpdateSavedFeedsMutation,
+} from '#/state/queries/preferences'
+import {useSetSelectedFeed} from '#/state/shell/selected-feed'
+import * as Toast from '#/view/com/util/Toast'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+import {InlineLinkText} from '#/components/Link'
+import {Loader} from '#/components/Loader'
+import {Text} from '#/components/Typography'
+
+export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
+  const t = useTheme()
+  const {_} = useLingui()
+  const setSelectedFeed = useSetSelectedFeed()
+  const {data: preferences} = usePreferencesQuery()
+  const {mutateAsync: addSavedFeeds, isPending: isAddSavedFeedPending} =
+    useAddSavedFeedsMutation()
+  const {mutateAsync: removeFeed, isPending: isRemovePending} =
+    useRemoveFeedMutation()
+  const {mutateAsync: updateSavedFeeds, isPending: isUpdateFeedPending} =
+    useUpdateSavedFeedsMutation()
+
+  const feedConfig = preferences?.savedFeeds?.find(
+    f => f.value === feedUri && f.pinned,
+  )
+  const discoverFeedConfig = preferences?.savedFeeds?.find(
+    f => f.value === PROD_DEFAULT_FEED('whats-hot'),
+  )
+  const hasFeedPinned = Boolean(feedConfig)
+  const hasDiscoverPinned = Boolean(discoverFeedConfig?.pinned)
+
+  const onRemoveFeed = React.useCallback(async () => {
+    try {
+      if (feedConfig) {
+        await removeFeed(feedConfig)
+        Toast.show(_(msg`Removed from your feeds`))
+      }
+    } catch (err: any) {
+      Toast.show(
+        _(
+          msg`There was an an issue updating your feeds, please check your internet connection and try again.`,
+        ),
+      )
+      logger.error('Failed up update feeds', {message: err})
+    }
+  }, [removeFeed, feedConfig, _])
+
+  const onReplaceFeed = React.useCallback(async () => {
+    try {
+      if (!discoverFeedConfig) {
+        await addSavedFeeds([
+          {
+            type: 'feed',
+            value: PROD_DEFAULT_FEED('whats-hot'),
+            pinned: true,
+          },
+        ])
+      } else {
+        await updateSavedFeeds([
+          {
+            ...discoverFeedConfig,
+            pinned: true,
+          },
+        ])
+      }
+      setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
+      if (feedConfig) {
+        await removeFeed(feedConfig)
+      }
+      Toast.show(_(msg`The feed has been replaced with Discover.`))
+    } catch (err: any) {
+      Toast.show(
+        _(
+          msg`There was an an issue updating your feeds, please check your internet connection and try again.`,
+        ),
+      )
+      logger.error('Failed up update feeds', {message: err})
+    }
+  }, [
+    addSavedFeeds,
+    updateSavedFeeds,
+    removeFeed,
+    discoverFeedConfig,
+    feedConfig,
+    setSelectedFeed,
+    _,
+  ])
+
+  const isProcessing =
+    isAddSavedFeedPending || isUpdateFeedPending || isRemovePending
+  return (
+    <View
+      style={[
+        a.py_3xl,
+        a.px_2xl,
+        a.gap_xl,
+        t.atoms.border_contrast_low,
+        a.border_t,
+      ]}>
+      <Text style={[a.text_5xl, a.font_bold, t.atoms.text, a.text_center]}>
+        :(
+      </Text>
+      <Text style={[a.text_md, a.leading_snug, t.atoms.text, a.text_center]}>
+        <Trans>
+          This feed is no longer online. We are showing{' '}
+          <InlineLinkText
+            to="/profile/bsky.app/feed/whats-hot"
+            style={[a.text_md]}>
+            Discover
+          </InlineLinkText>{' '}
+          instead.
+        </Trans>
+      </Text>
+      {hasFeedPinned ? (
+        <View style={[a.flex_row, a.justify_center, a.gap_sm]}>
+          <Button
+            variant="outline"
+            color="primary"
+            size="small"
+            label={_(msg`Remove feed`)}
+            disabled={isProcessing}
+            onPress={onRemoveFeed}>
+            <ButtonText>
+              <Trans>Remove feed</Trans>
+            </ButtonText>
+            {isRemovePending && <ButtonIcon icon={Loader} />}
+          </Button>
+          {!hasDiscoverPinned && (
+            <Button
+              variant="solid"
+              color="primary"
+              size="small"
+              label={_(msg`Replace with Discover`)}
+              disabled={isProcessing}
+              onPress={onReplaceFeed}>
+              <ButtonText>
+                <Trans>Replace with Discover</Trans>
+              </ButtonText>
+              {(isAddSavedFeedPending || isUpdateFeedPending) && (
+                <ButtonIcon icon={Loader} />
+              )}
+            </Button>
+          )}
+        </View>
+      ) : undefined}
+    </View>
+  )
+}