about summary refs log tree commit diff
path: root/src/view/shell/desktop
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-12-17 21:45:39 -0600
committerGitHub <noreply@github.com>2024-12-17 19:45:39 -0800
commita2019aceec001e276272832b97ea5e2ec864c8a5 (patch)
treeeaddab8a7a009650d93bb3b49c750619d98bb44d /src/view/shell/desktop
parenta07949ec8e63bae178a829f65c33fcd9622b28ec (diff)
downloadvoidsky-a2019aceec001e276272832b97ea5e2ec864c8a5.tar.zst
Trending (Beta) (#7144)
* Add WIP UIs for trending topics and suggested starterpacks

* Disable SPs for now

* Improve explore treatment a bit, add some polish to cards

* Add tiny option in RightNav

* Add persisted option to hide trending from sidebar

* Add to settings, abstract state, not updating in tab

* Fix up hide/show toggle state, WITH broadcast hacK

* Clean up persisted code, add new setting

* Add new interstitial to Discover

* Exploration

* First hack at mute words

* Wire up interstitial and Explore page

* Align components

* Some skeleton UI

* Handle service config, enablement, load states, update lex contract

* Centralize mute word handling

* Stale time to 30m

* Cache enabled value for reloads, use real data for service config

* Remove broadcast hack

* Remove titleChild

* Gate settings too

* Update package, rm langs

* Add feature gate

* Only english during beta period

* Hook up real data

* Tweak config

* Straight passthrough links

* Hook up prod agent

* Fix no-show logic

* Up config query to 5 min

* Remove old file

* Remove comment

* Remove stray flex_1

* Make trending setting global

* Quick placeholder state

* Limit # in sidebar, tweak spacing

* Tweak gaps

* Handle hide/show of sidebar

* Simplify messages

* Remove interstitial

* Revert "Remove interstitial"

This reverts commit 1358ad47fdf7e633749340c410933b508af46c10.

* Only show interstitial on mobile

* Fix gap

* Add explore page recommendations

* [topics] add topic screen (#7149)

* add topic screen

* decode

* fix search query

* decode

* add server route

* Fix potential bad destructure (undefined)

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/view/shell/desktop')
-rw-r--r--src/view/shell/desktop/Feeds.tsx32
-rw-r--r--src/view/shell/desktop/RightNav.tsx44
-rw-r--r--src/view/shell/desktop/SidebarTrendingTopics.tsx104
3 files changed, 167 insertions, 13 deletions
diff --git a/src/view/shell/desktop/Feeds.tsx b/src/view/shell/desktop/Feeds.tsx
index 83b5420ce..1d515df55 100644
--- a/src/view/shell/desktop/Feeds.tsx
+++ b/src/view/shell/desktop/Feeds.tsx
@@ -14,7 +14,7 @@ import {createStaticClick, InlineLinkText} from '#/components/Link'
 export function DesktopFeeds() {
   const t = useTheme()
   const {_} = useLingui()
-  const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
+  const {data: pinnedFeedInfos, error, isLoading} = usePinnedFeedsInfos()
   const selectedFeed = useSelectedFeed()
   const setSelectedFeed = useSetSelectedFeed()
   const navigation = useNavigation<NavigationProp>()
@@ -25,14 +25,40 @@ export function DesktopFeeds() {
     return getCurrentRoute(state)
   })
 
-  if (!pinnedFeedInfos) {
+  if (isLoading) {
+    return (
+      <View
+        style={[
+          {
+            gap: 12,
+          },
+        ]}>
+        {Array(5)
+          .fill(0)
+          .map((_, i) => (
+            <View
+              key={i}
+              style={[
+                a.rounded_sm,
+                t.atoms.bg_contrast_25,
+                {
+                  height: 16,
+                  width: i % 2 === 0 ? '60%' : '80%',
+                },
+              ]}
+            />
+          ))}
+      </View>
+    )
+  }
+
+  if (error || !pinnedFeedInfos) {
     return null
   }
 
   return (
     <View
       style={[
-        a.flex_1,
         web({
           gap: 10,
           /*
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 895d16021..363294aa5 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -1,6 +1,8 @@
+import React from 'react'
 import {View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {useNavigation} from '@react-navigation/core'
 
 import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
@@ -8,17 +10,41 @@ import {useKawaiiMode} from '#/state/preferences/kawaii'
 import {useSession} from '#/state/session'
 import {DesktopFeeds} from '#/view/shell/desktop/Feeds'
 import {DesktopSearch} from '#/view/shell/desktop/Search'
+import {SidebarTrendingTopics} from '#/view/shell/desktop/SidebarTrendingTopics'
 import {atoms as a, useGutters, useTheme, web} from '#/alf'
+import {Divider} from '#/components/Divider'
 import {InlineLinkText} from '#/components/Link'
 import {ProgressGuideList} from '#/components/ProgressGuide/List'
 import {Text} from '#/components/Typography'
 
+function useWebQueryParams() {
+  const navigation = useNavigation()
+  const [params, setParams] = React.useState<Record<string, string>>({})
+
+  React.useEffect(() => {
+    return navigation.addListener('state', e => {
+      try {
+        const {state} = e.data
+        const lastRoute = state.routes[state.routes.length - 1]
+        const {params} = lastRoute
+        setParams(params)
+      } catch (e) {}
+    })
+  }, [navigation, setParams])
+
+  return params
+}
+
 export function DesktopRightNav({routeName}: {routeName: string}) {
   const t = useTheme()
   const {_} = useLingui()
   const {hasSession, currentAccount} = useSession()
   const kawaii = useKawaiiMode()
   const gutters = useGutters(['base', 0, 'base', 'wide'])
+  const isSearchScreen = routeName === 'Search'
+  const webqueryParams = useWebQueryParams()
+  const searchQuery = webqueryParams?.q
+  const showTrending = !isSearchScreen || (isSearchScreen && !!searchQuery)
 
   const {isTablet} = useWebMediaQueries()
   if (isTablet) {
@@ -29,6 +55,7 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
     <View
       style={[
         gutters,
+        a.gap_lg,
         web({
           position: 'fixed',
           left: '50%',
@@ -43,21 +70,18 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
           overflowY: 'auto',
         }),
       ]}>
-      {routeName !== 'Search' && (
-        <View style={[a.pb_lg]}>
-          <DesktopSearch />
-        </View>
-      )}
+      {!isSearchScreen && <DesktopSearch />}
+
       {hasSession && (
         <>
-          <ProgressGuideList style={[a.pb_xl]} />
-          <View
-            style={[a.pb_lg, a.mb_lg, a.border_b, t.atoms.border_contrast_low]}>
-            <DesktopFeeds />
-          </View>
+          <ProgressGuideList />
+          <DesktopFeeds />
+          <Divider />
         </>
       )}
 
+      {showTrending && <SidebarTrendingTopics />}
+
       <Text style={[a.leading_snug, t.atoms.text_contrast_low]}>
         {hasSession && (
           <>
diff --git a/src/view/shell/desktop/SidebarTrendingTopics.tsx b/src/view/shell/desktop/SidebarTrendingTopics.tsx
new file mode 100644
index 000000000..e22fad54d
--- /dev/null
+++ b/src/view/shell/desktop/SidebarTrendingTopics.tsx
@@ -0,0 +1,104 @@
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {
+  useTrendingSettings,
+  useTrendingSettingsApi,
+} from '#/state/preferences/trending'
+import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
+import {useTrendingConfig} from '#/state/trending-config'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonIcon} from '#/components/Button'
+import {Divider} from '#/components/Divider'
+import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
+import {Trending2_Stroke2_Corner2_Rounded as Graph} from '#/components/icons/Trending2'
+import * as Prompt from '#/components/Prompt'
+import {
+  TrendingTopic,
+  TrendingTopicLink,
+  TrendingTopicSkeleton,
+} from '#/components/TrendingTopics'
+import {Text} from '#/components/Typography'
+
+const TRENDING_LIMIT = 6
+
+export function SidebarTrendingTopics() {
+  const {enabled} = useTrendingConfig()
+  const {trendingDisabled} = useTrendingSettings()
+  return !enabled ? null : trendingDisabled ? null : <Inner />
+}
+
+function Inner() {
+  const t = useTheme()
+  const {_} = useLingui()
+  const trendingPrompt = Prompt.usePromptControl()
+  const {setTrendingDisabled} = useTrendingSettingsApi()
+  const {data: trending, error, isLoading} = useTrendingTopics()
+  const noTopics = !isLoading && !error && !trending?.topics?.length
+
+  return error || noTopics ? null : (
+    <>
+      <View style={[a.gap_sm, {paddingBottom: 2}]}>
+        <View style={[a.flex_row, a.align_center, a.gap_xs]}>
+          <Graph size="sm" />
+          <Text
+            style={[
+              a.flex_1,
+              a.text_sm,
+              a.font_bold,
+              t.atoms.text_contrast_medium,
+            ]}>
+            <Trans>Trending</Trans>
+          </Text>
+          <Button
+            label={_(msg`Hide trending topics`)}
+            size="tiny"
+            variant="ghost"
+            color="secondary"
+            shape="round"
+            onPress={() => trendingPrompt.open()}>
+            <ButtonIcon icon={X} />
+          </Button>
+        </View>
+
+        <View style={[a.flex_row, a.flex_wrap, {gap: '6px 4px'}]}>
+          {isLoading ? (
+            Array(TRENDING_LIMIT)
+              .fill(0)
+              .map((_n, i) => (
+                <TrendingTopicSkeleton key={i} size="small" index={i} />
+              ))
+          ) : !trending?.topics ? null : (
+            <>
+              {trending.topics.slice(0, TRENDING_LIMIT).map(topic => (
+                <TrendingTopicLink key={topic.link} topic={topic}>
+                  {({hovered}) => (
+                    <TrendingTopic
+                      size="small"
+                      topic={topic}
+                      style={[
+                        hovered && [
+                          t.atoms.border_contrast_high,
+                          t.atoms.bg_contrast_25,
+                        ],
+                      ]}
+                    />
+                  )}
+                </TrendingTopicLink>
+              ))}
+            </>
+          )}
+        </View>
+      </View>
+      <Prompt.Basic
+        control={trendingPrompt}
+        title={_(msg`Hide trending topics?`)}
+        description={_(msg`You can update this later from your settings.`)}
+        confirmButtonCta={_(msg`Hide`)}
+        onConfirm={() => setTrendingDisabled(true)}
+      />
+      <Divider />
+    </>
+  )
+}