From 0fd5c9294a6bf75ecb657ab6974096a5013047d6 Mon Sep 17 00:00:00 2001
From: Ansh Nanda <anshnanda10@gmail.com>
Date: Tue, 23 May 2023 15:33:27 -0700
Subject: refactor load latest btn

---
 src/view/com/util/load-latest/LoadLatestBtnMobile.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/view/com/util/load-latest/LoadLatestBtnMobile.tsx')

diff --git a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
index 5279696a2..548d30d5a 100644
--- a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
@@ -25,15 +25,15 @@ export const LoadLatestBtn = observer(
         onPress={onPress}
         hitSlop={HITSLOP}
         accessibilityRole="button"
-        accessibilityLabel={`Load new ${label}`}
-        accessibilityHint={`Loads new ${label}`}>
+        accessibilityLabel={label}
+        accessibilityHint={label}>
         <LinearGradient
           colors={[gradients.blueLight.start, gradients.blueLight.end]}
           start={{x: 0, y: 0}}
           end={{x: 1, y: 1}}
           style={styles.loadLatestInner}>
           <Text type="md-bold" style={styles.loadLatestText}>
-            Load new {label}
+            {label}
           </Text>
         </LinearGradient>
       </TouchableOpacity>
-- 
cgit 1.4.1


From 4e1876fe85ab3a70eba50466a62bff8a9d01c16c Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Wed, 24 May 2023 18:46:27 -0500
Subject: Refactor the scroll-to-top UX

---
 src/lib/hooks/useOnMainScroll.ts                   | 58 ++++++++++++------
 src/view/com/notifications/Feed.tsx                |  1 +
 src/view/com/posts/Feed.tsx                        |  3 +-
 src/view/com/util/fab/FABInner.tsx                 |  2 +-
 .../com/util/load-latest/LoadLatestBtnMobile.tsx   | 39 +++++-------
 src/view/screens/CustomFeed.tsx                    | 70 +++++++++-------------
 src/view/screens/Home.tsx                          | 11 ++--
 src/view/screens/Notifications.tsx                 | 16 +++--
 src/view/screens/SearchMobile.tsx                  |  2 +-
 9 files changed, 102 insertions(+), 100 deletions(-)

(limited to 'src/view/com/util/load-latest/LoadLatestBtnMobile.tsx')

diff --git a/src/lib/hooks/useOnMainScroll.ts b/src/lib/hooks/useOnMainScroll.ts
index 994a35714..782c4704b 100644
--- a/src/lib/hooks/useOnMainScroll.ts
+++ b/src/lib/hooks/useOnMainScroll.ts
@@ -1,28 +1,50 @@
-import {useState} from 'react'
+import {useState, useCallback, useRef} from 'react'
 import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
 import {RootStoreModel} from 'state/index'
+import {s} from 'lib/styles'
 
-export type onMomentumScrollEndCb = (
-  event: NativeSyntheticEvent<NativeScrollEvent>,
-) => void
 export type OnScrollCb = (
   event: NativeSyntheticEvent<NativeScrollEvent>,
 ) => void
+export type ResetCb = () => void
+
+export function useOnMainScroll(
+  store: RootStoreModel,
+): [OnScrollCb, boolean, ResetCb] {
+  let lastY = useRef(0)
+  let [isScrolledDown, setIsScrolledDown] = useState(false)
+  return [
+    useCallback(
+      (event: NativeSyntheticEvent<NativeScrollEvent>) => {
+        const y = event.nativeEvent.contentOffset.y
+        const dy = y - (lastY.current || 0)
+        lastY.current = y
 
-export function useOnMainScroll(store: RootStoreModel) {
-  let [lastY, setLastY] = useState(0)
-  let isMinimal = store.shell.minimalShellMode
-  return function onMainScroll(event: NativeSyntheticEvent<NativeScrollEvent>) {
-    const y = event.nativeEvent.contentOffset.y
-    const dy = y - (lastY || 0)
-    setLastY(y)
+        if (!store.shell.minimalShellMode && y > 10 && dy > 10) {
+          store.shell.setMinimalShellMode(true)
+        } else if (store.shell.minimalShellMode && (y <= 10 || dy < -10)) {
+          store.shell.setMinimalShellMode(false)
+        }
 
-    if (!isMinimal && y > 10 && dy > 10) {
-      store.shell.setMinimalShellMode(true)
-      isMinimal = true
-    } else if (isMinimal && (y <= 10 || dy < -10)) {
+        if (
+          !isScrolledDown &&
+          event.nativeEvent.contentOffset.y > s.window.height
+        ) {
+          setIsScrolledDown(true)
+        } else if (
+          isScrolledDown &&
+          event.nativeEvent.contentOffset.y < s.window.height
+        ) {
+          setIsScrolledDown(false)
+        }
+      },
+      [store, isScrolledDown],
+    ),
+    isScrolledDown,
+    useCallback(() => {
+      setIsScrolledDown(false)
       store.shell.setMinimalShellMode(false)
-      isMinimal = false
-    }
-  }
+      lastY.current = 1e8 // NOTE we set this very high so that the onScroll logic works right -prf
+    }, [store, setIsScrolledDown]),
+  ]
 }
diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx
index 50bdc5dc9..d457d7136 100644
--- a/src/view/com/notifications/Feed.tsx
+++ b/src/view/com/notifications/Feed.tsx
@@ -154,6 +154,7 @@ export const Feed = observer(function Feed({
           onEndReached={onEndReached}
           onEndReachedThreshold={0.6}
           onScroll={onScroll}
+          scrollEventThrottle={100}
           contentContainerStyle={s.contentContainer}
         />
       ) : null}
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index 2726ff7d3..b90213472 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -14,7 +14,7 @@ import {ErrorMessage} from '../util/error/ErrorMessage'
 import {PostsFeedModel} from 'state/models/feeds/posts'
 import {FeedSlice} from './FeedSlice'
 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
-import {OnScrollCb, onMomentumScrollEndCb} from 'lib/hooks/useOnMainScroll'
+import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
 import {s} from 'lib/styles'
 import {useAnalytics} from 'lib/analytics'
 import {usePalette} from 'lib/hooks/usePalette'
@@ -47,7 +47,6 @@ export const Feed = observer(function Feed({
   onPressTryAgain?: () => void
   onScroll?: OnScrollCb
   scrollEventThrottle?: number
-  onMomentumScrollEnd?: onMomentumScrollEndCb
   renderEmptyState?: () => JSX.Element
   testID?: string
   headerOffset?: number
diff --git a/src/view/com/util/fab/FABInner.tsx b/src/view/com/util/fab/FABInner.tsx
index 5eb4a6588..76824e575 100644
--- a/src/view/com/util/fab/FABInner.tsx
+++ b/src/view/com/util/fab/FABInner.tsx
@@ -47,7 +47,7 @@ const styles = StyleSheet.create({
   outer: {
     position: 'absolute',
     zIndex: 1,
-    right: 28,
+    right: 24,
     bottom: 94,
     width: 60,
     height: 60,
diff --git a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
index 548d30d5a..5e03e2285 100644
--- a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
@@ -1,23 +1,25 @@
 import React from 'react'
 import {StyleSheet, TouchableOpacity} from 'react-native'
 import {observer} from 'mobx-react-lite'
-import LinearGradient from 'react-native-linear-gradient'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import {Text} from '../text/Text'
-import {colors, gradients} from 'lib/styles'
 import {clamp} from 'lodash'
 import {useStores} from 'state/index'
+import {usePalette} from 'lib/hooks/usePalette'
 
 const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
 
 export const LoadLatestBtn = observer(
   ({onPress, label}: {onPress: () => void; label: string}) => {
     const store = useStores()
+    const pal = usePalette('default')
     const safeAreaInsets = useSafeAreaInsets()
     return (
       <TouchableOpacity
         style={[
           styles.loadLatest,
+          pal.borderDark,
+          pal.view,
           !store.shell.minimalShellMode && {
             bottom: 60 + clamp(safeAreaInsets.bottom, 15, 30),
           },
@@ -26,16 +28,8 @@ export const LoadLatestBtn = observer(
         hitSlop={HITSLOP}
         accessibilityRole="button"
         accessibilityLabel={label}
-        accessibilityHint={label}>
-        <LinearGradient
-          colors={[gradients.blueLight.start, gradients.blueLight.end]}
-          start={{x: 0, y: 0}}
-          end={{x: 1, y: 1}}
-          style={styles.loadLatestInner}>
-          <Text type="md-bold" style={styles.loadLatestText}>
-            {label}
-          </Text>
-        </LinearGradient>
+        accessibilityHint="">
+        <FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
       </TouchableOpacity>
     )
   },
@@ -44,19 +38,14 @@ export const LoadLatestBtn = observer(
 const styles = StyleSheet.create({
   loadLatest: {
     position: 'absolute',
-    left: 20,
+    left: 18,
     bottom: 35,
-    shadowColor: '#000',
-    shadowOpacity: 0.3,
-    shadowOffset: {width: 0, height: 1},
-  },
-  loadLatestInner: {
+    borderWidth: 1,
+    width: 52,
+    height: 52,
+    borderRadius: 26,
     flexDirection: 'row',
-    paddingHorizontal: 14,
-    paddingVertical: 10,
-    borderRadius: 30,
-  },
-  loadLatestText: {
-    color: colors.white,
+    alignItems: 'center',
+    justifyContent: 'center',
   },
 })
diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx
index dcb726873..1409762d1 100644
--- a/src/view/screens/CustomFeed.tsx
+++ b/src/view/screens/CustomFeed.tsx
@@ -20,13 +20,13 @@ import {ViewHeader} from 'view/com/util/ViewHeader'
 import {Button} from 'view/com/util/forms/Button'
 import {Text} from 'view/com/util/text/Text'
 import * as Toast from 'view/com/util/Toast'
-import {isDesktopWeb, isWeb} from 'platform/detection'
+import {isDesktopWeb} from 'platform/detection'
 import {useSetTitle} from 'lib/hooks/useSetTitle'
 import {shareUrl} from 'lib/sharing'
 import {toShareUrl} from 'lib/strings/url-helpers'
 import {Haptics} from 'lib/haptics'
 import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
-import {OnScrollCb, onMomentumScrollEndCb} from 'lib/hooks/useOnMainScroll'
+import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'>
 export const CustomFeedScreen = withAuthRequired(
@@ -48,7 +48,8 @@ export const CustomFeedScreen = withAuthRequired(
       return feed
     }, [store, uri])
     const isPinned = store.me.savedFeeds.isPinned(uri)
-    const [allowScrollToTop, setAllowScrollToTop] = useState(false)
+    const [onMainScroll, isScrolledDown, resetMainScroll] =
+      useOnMainScroll(store)
     useSetTitle(currentFeed?.displayName)
 
     const onToggleSaved = React.useCallback(async () => {
@@ -66,6 +67,7 @@ export const CustomFeedScreen = withAuthRequired(
         store.log.error('Failed up update feeds', {err})
       }
     }, [store, currentFeed])
+
     const onToggleLiked = React.useCallback(async () => {
       Haptics.default()
       try {
@@ -81,6 +83,7 @@ export const CustomFeedScreen = withAuthRequired(
         store.log.error('Failed up toggle like', {err})
       }
     }, [store, currentFeed])
+
     const onTogglePinned = React.useCallback(async () => {
       Haptics.default()
       store.me.savedFeeds.togglePinnedFeed(currentFeed!).catch(e => {
@@ -88,11 +91,17 @@ export const CustomFeedScreen = withAuthRequired(
         store.log.error('Failed to toggle pinned feed', {e})
       })
     }, [store, currentFeed])
+
     const onPressShare = React.useCallback(() => {
       const url = toShareUrl(`/profile/${name}/feed/${rkey}`)
       shareUrl(url)
     }, [name, rkey])
 
+    const onScrollToTop = React.useCallback(() => {
+      scrollElRef.current?.scrollToOffset({offset: 0, animated: true})
+      resetMainScroll()
+    }, [scrollElRef, resetMainScroll])
+
     const renderHeaderBtns = React.useCallback(() => {
       return (
         <View style={styles.headerBtns}>
@@ -220,15 +229,17 @@ export const CustomFeedScreen = withAuthRequired(
               </Text>
             ) : null}
             <View style={styles.headerDetailsFooter}>
-              <TextLink
-                type="md-medium"
-                style={pal.textLight}
-                href={`/profile/${name}/feed/${rkey}/liked-by`}
-                text={`Liked by ${currentFeed?.data.likeCount} ${pluralize(
-                  currentFeed?.data.likeCount || 0,
-                  'user',
-                )}`}
-              />
+              {currentFeed ? (
+                <TextLink
+                  type="md-medium"
+                  style={pal.textLight}
+                  href={`/profile/${name}/feed/${rkey}/liked-by`}
+                  text={`Liked by ${currentFeed.data.likeCount} ${pluralize(
+                    currentFeed?.data.likeCount || 0,
+                    'user',
+                  )}`}
+                />
+              ) : null}
               <Button
                 type={'default'}
                 accessibilityLabel={
@@ -267,46 +278,19 @@ export const CustomFeedScreen = withAuthRequired(
       onTogglePinned,
     ])
 
-    const onMomentumScrollEnd: onMomentumScrollEndCb = React.useCallback(
-      event => {
-        console.log('onMomentumScrollEnd')
-        if (event.nativeEvent.contentOffset.y > s.window.height * 3) {
-          setAllowScrollToTop(true)
-        } else {
-          setAllowScrollToTop(false)
-        }
-      },
-      [],
-    )
-    const onScroll: OnScrollCb = React.useCallback(event => {
-      // since onMomentumScrollEnd is not supported in react-native-web, we have to use onScroll which fires more often so is not desirable on mobile
-      if (isWeb) {
-        if (event.nativeEvent.contentOffset.y > s.window.height * 2) {
-          setAllowScrollToTop(true)
-        } else {
-          setAllowScrollToTop(false)
-        }
-      }
-    }, [])
-
     return (
       <View style={s.hContentRegion}>
         <ViewHeader title="" renderButton={renderHeaderBtns} />
         <Feed
           scrollElRef={scrollElRef}
           feed={algoFeed}
-          onMomentumScrollEnd={onMomentumScrollEnd}
-          onScroll={onScroll} // same logic as onMomentumScrollEnd but for web
+          onScroll={onMainScroll}
+          scrollEventThrottle={100}
           ListHeaderComponent={renderListHeaderComponent}
           extraData={[uri, isPinned]}
         />
-        {allowScrollToTop ? (
-          <LoadLatestBtn
-            onPress={() => {
-              scrollElRef.current?.scrollToOffset({offset: 0, animated: true})
-            }}
-            label="Scroll to top"
-          />
+        {isScrolledDown ? (
+          <LoadLatestBtn onPress={onScrollToTop} label="Scroll to top" />
         ) : null}
       </View>
     )
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 4fe175fc1..bd800590d 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -150,7 +150,8 @@ const FeedPage = observer(
     renderEmptyState?: () => JSX.Element
   }) => {
     const store = useStores()
-    const onMainScroll = useOnMainScroll(store)
+    const [onMainScroll, isScrolledDown, resetMainScroll] =
+      useOnMainScroll(store)
     const {screen, track} = useAnalytics()
     const scrollElRef = React.useRef<FlatList>(null)
     const {appState} = useAppState({
@@ -178,12 +179,13 @@ const FeedPage = observer(
 
     const scrollToTop = React.useCallback(() => {
       scrollElRef.current?.scrollToOffset({offset: -HEADER_OFFSET})
-    }, [scrollElRef])
+      resetMainScroll()
+    }, [scrollElRef, resetMainScroll])
 
     const onSoftReset = React.useCallback(() => {
       if (isPageFocused) {
-        feed.refresh()
         scrollToTop()
+        feed.refresh()
       }
     }, [isPageFocused, scrollToTop, feed])
 
@@ -254,10 +256,11 @@ const FeedPage = observer(
           showPostFollowBtn
           onPressTryAgain={onPressTryAgain}
           onScroll={onMainScroll}
+          scrollEventThrottle={100}
           renderEmptyState={renderEmptyState}
           headerOffset={HEADER_OFFSET}
         />
-        {feed.hasNewLatest && !feed.isRefreshing && (
+        {isScrolledDown && (
           <LoadLatestBtn onPress={onPressLoadLatest} label="Load new posts" />
         )}
         <FAB
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index df84b541b..02a4618c3 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -25,7 +25,8 @@ type Props = NativeStackScreenProps<
 export const NotificationsScreen = withAuthRequired(
   observer(({}: Props) => {
     const store = useStores()
-    const onMainScroll = useOnMainScroll(store)
+    const [onMainScroll, isScrolledDown, resetMainScroll] =
+      useOnMainScroll(store)
     const scrollElRef = React.useRef<FlatList>(null)
     const {screen} = useAnalytics()
 
@@ -37,7 +38,8 @@ export const NotificationsScreen = withAuthRequired(
 
     const scrollToTop = React.useCallback(() => {
       scrollElRef.current?.scrollToOffset({offset: 0})
-    }, [scrollElRef])
+      resetMainScroll()
+    }, [scrollElRef, resetMainScroll])
 
     const onPressLoadLatest = React.useCallback(() => {
       scrollToTop()
@@ -96,10 +98,12 @@ export const NotificationsScreen = withAuthRequired(
           onScroll={onMainScroll}
           scrollElRef={scrollElRef}
         />
-        {store.me.notifications.hasNewLatest &&
-          !store.me.notifications.isRefreshing && (
-            <LoadLatestBtn onPress={onPressLoadLatest} label="Load new notifications" />
-          )}
+        {isScrolledDown && (
+          <LoadLatestBtn
+            onPress={onPressLoadLatest}
+            label="Load new notifications"
+          />
+        )}
       </View>
     )
   }),
diff --git a/src/view/screens/SearchMobile.tsx b/src/view/screens/SearchMobile.tsx
index f9b4864b2..c9d09373e 100644
--- a/src/view/screens/SearchMobile.tsx
+++ b/src/view/screens/SearchMobile.tsx
@@ -35,7 +35,7 @@ export const SearchScreen = withAuthRequired(
     const store = useStores()
     const scrollViewRef = React.useRef<ScrollView>(null)
     const flatListRef = React.useRef<FlatList>(null)
-    const onMainScroll = useOnMainScroll(store)
+    const [onMainScroll] = useOnMainScroll(store)
     const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false)
     const [query, setQuery] = React.useState<string>('')
     const autocompleteView = React.useMemo<UserAutocompleteModel>(
-- 
cgit 1.4.1


From e89103915db48fc4306b553877be1ffa8ef94e91 Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Thu, 25 May 2023 01:30:48 -0500
Subject: Update load latest to show when there's new activity with an
 indicator

---
 src/state/models/feeds/notifications.ts            |  4 +++-
 .../com/util/load-latest/LoadLatestBtn.web.tsx     | 24 ++++++++++++++++++---
 .../com/util/load-latest/LoadLatestBtnMobile.tsx   | 25 ++++++++++++++++++++--
 src/view/screens/Home.tsx                          |  9 ++++++--
 src/view/screens/Notifications.tsx                 |  6 +++++-
 5 files changed, 59 insertions(+), 9 deletions(-)

(limited to 'src/view/com/util/load-latest/LoadLatestBtnMobile.tsx')

diff --git a/src/state/models/feeds/notifications.ts b/src/state/models/feeds/notifications.ts
index 73424f03e..3777abb92 100644
--- a/src/state/models/feeds/notifications.ts
+++ b/src/state/models/feeds/notifications.ts
@@ -290,7 +290,9 @@ export class NotificationsFeedModel {
   }
 
   get hasNewLatest() {
-    return this.queuedNotifications && this.queuedNotifications?.length > 0
+    return Boolean(
+      this.queuedNotifications && this.queuedNotifications?.length > 0,
+    )
   }
 
   get unreadCountLabel(): string {
diff --git a/src/view/com/util/load-latest/LoadLatestBtn.web.tsx b/src/view/com/util/load-latest/LoadLatestBtn.web.tsx
index 7a3e55d7d..0b4e649f9 100644
--- a/src/view/com/util/load-latest/LoadLatestBtn.web.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtn.web.tsx
@@ -1,24 +1,32 @@
 import React from 'react'
-import {StyleSheet, TouchableOpacity} from 'react-native'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Text} from '../text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
-import {UpIcon} from 'lib/icons'
 import {LoadLatestBtn as LoadLatestBtnMobile} from './LoadLatestBtnMobile'
 import {isMobileWeb} from 'platform/detection'
+import {colors} from 'lib/styles'
 
 const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
 
 export const LoadLatestBtn = ({
   onPress,
   label,
+  showIndicator,
 }: {
   onPress: () => void
   label: string
+  showIndicator: boolean
 }) => {
   const pal = usePalette('default')
   if (isMobileWeb) {
-    return <LoadLatestBtnMobile onPress={onPress} label={label} />
+    return (
+      <LoadLatestBtnMobile
+        onPress={onPress}
+        label={label}
+        showIndicator={showIndicator}
+      />
+    )
   }
   return (
     <TouchableOpacity
@@ -35,6 +43,7 @@ export const LoadLatestBtn = ({
           style={[pal.text, styles.icon]}
         />
       </Text>
+      {showIndicator && <View style={styles.indicator} />}
     </TouchableOpacity>
   )
 }
@@ -58,4 +67,13 @@ const styles = StyleSheet.create({
     position: 'relative',
     top: 2,
   },
+  indicator: {
+    position: 'absolute',
+    top: 3,
+    right: 3,
+    backgroundColor: colors.blue3,
+    width: 10,
+    height: 10,
+    borderRadius: 6,
+  },
 })
diff --git a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
index 5e03e2285..7d1823a13 100644
--- a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
@@ -1,16 +1,26 @@
 import React from 'react'
-import {StyleSheet, TouchableOpacity} from 'react-native'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {clamp} from 'lodash'
 import {useStores} from 'state/index'
 import {usePalette} from 'lib/hooks/usePalette'
+import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
+import {colors} from 'lib/styles'
 
 const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
 
 export const LoadLatestBtn = observer(
-  ({onPress, label}: {onPress: () => void; label: string}) => {
+  ({
+    onPress,
+    label,
+    showIndicator,
+  }: {
+    onPress: () => void
+    label: string
+    showIndicator: boolean
+  }) => {
     const store = useStores()
     const pal = usePalette('default')
     const safeAreaInsets = useSafeAreaInsets()
@@ -30,6 +40,7 @@ export const LoadLatestBtn = observer(
         accessibilityLabel={label}
         accessibilityHint="">
         <FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
+        {showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
       </TouchableOpacity>
     )
   },
@@ -48,4 +59,14 @@ const styles = StyleSheet.create({
     alignItems: 'center',
     justifyContent: 'center',
   },
+  indicator: {
+    position: 'absolute',
+    top: 3,
+    right: 3,
+    backgroundColor: colors.blue3,
+    width: 12,
+    height: 12,
+    borderRadius: 6,
+    borderWidth: 1,
+  },
 })
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index bd800590d..2c03f1930 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -246,6 +246,7 @@ const FeedPage = observer(
       feed.refresh()
     }, [feed, scrollToTop])
 
+    const hasNew = feed.hasNewLatest && !feed.isRefreshing
     return (
       <View testID={testID} style={s.h100pct}>
         <Feed
@@ -260,8 +261,12 @@ const FeedPage = observer(
           renderEmptyState={renderEmptyState}
           headerOffset={HEADER_OFFSET}
         />
-        {isScrolledDown && (
-          <LoadLatestBtn onPress={onPressLoadLatest} label="Load new posts" />
+        {(isScrolledDown || hasNew) && (
+          <LoadLatestBtn
+            onPress={onPressLoadLatest}
+            label="Load new posts"
+            showIndicator={hasNew}
+          />
         )}
         <FAB
           testID="composeFAB"
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 02a4618c3..64dcb3a1a 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -88,6 +88,9 @@ export const NotificationsScreen = withAuthRequired(
       ),
     )
 
+    const hasNew =
+      store.me.notifications.hasNewLatest &&
+      !store.me.notifications.isRefreshing
     return (
       <View testID="notificationsScreen" style={s.hContentRegion}>
         <ViewHeader title="Notifications" canGoBack={false} />
@@ -98,10 +101,11 @@ export const NotificationsScreen = withAuthRequired(
           onScroll={onMainScroll}
           scrollElRef={scrollElRef}
         />
-        {isScrolledDown && (
+        {(isScrolledDown || hasNew) && (
           <LoadLatestBtn
             onPress={onPressLoadLatest}
             label="Load new notifications"
+            showIndicator={hasNew}
           />
         )}
       </View>
-- 
cgit 1.4.1


From 93e9fa8edfe1d8a046399a2b4b31bba4d69814f8 Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Thu, 25 May 2023 01:53:37 -0500
Subject: Restore the 'load more' button on web

---
 .../com/util/load-latest/LoadLatestBtn.web.tsx     | 78 +++++++++++++++-------
 .../com/util/load-latest/LoadLatestBtnMobile.tsx   |  2 +-
 src/view/screens/Home.tsx                          |  1 +
 src/view/screens/Notifications.tsx                 |  1 +
 4 files changed, 56 insertions(+), 26 deletions(-)

(limited to 'src/view/com/util/load-latest/LoadLatestBtnMobile.tsx')

diff --git a/src/view/com/util/load-latest/LoadLatestBtn.web.tsx b/src/view/com/util/load-latest/LoadLatestBtn.web.tsx
index 0b4e649f9..fefc540c0 100644
--- a/src/view/com/util/load-latest/LoadLatestBtn.web.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtn.web.tsx
@@ -1,11 +1,10 @@
 import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {StyleSheet, TouchableOpacity} from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Text} from '../text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
 import {LoadLatestBtn as LoadLatestBtnMobile} from './LoadLatestBtnMobile'
 import {isMobileWeb} from 'platform/detection'
-import {colors} from 'lib/styles'
 
 const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
 
@@ -13,10 +12,12 @@ export const LoadLatestBtn = ({
   onPress,
   label,
   showIndicator,
+  minimalShellMode,
 }: {
   onPress: () => void
   label: string
   showIndicator: boolean
+  minimalShellMode?: boolean
 }) => {
   const pal = usePalette('default')
   if (isMobileWeb) {
@@ -29,22 +30,41 @@ export const LoadLatestBtn = ({
     )
   }
   return (
-    <TouchableOpacity
-      style={[pal.view, pal.borderDark, styles.loadLatest]}
-      onPress={onPress}
-      hitSlop={HITSLOP}
-      accessibilityRole="button"
-      accessibilityLabel={label}
-      accessibilityHint="">
-      <Text type="md-bold" style={pal.text}>
-        <FontAwesomeIcon
-          icon="angle-up"
-          size={21}
-          style={[pal.text, styles.icon]}
-        />
-      </Text>
-      {showIndicator && <View style={styles.indicator} />}
-    </TouchableOpacity>
+    <>
+      {showIndicator && (
+        <TouchableOpacity
+          style={[
+            pal.view,
+            pal.borderDark,
+            styles.loadLatestCentered,
+            minimalShellMode && styles.loadLatestCenteredMinimal,
+          ]}
+          onPress={onPress}
+          hitSlop={HITSLOP}
+          accessibilityRole="button"
+          accessibilityLabel={label}
+          accessibilityHint="">
+          <Text type="md-bold" style={pal.text}>
+            {label}
+          </Text>
+        </TouchableOpacity>
+      )}
+      <TouchableOpacity
+        style={[pal.view, pal.borderDark, styles.loadLatest]}
+        onPress={onPress}
+        hitSlop={HITSLOP}
+        accessibilityRole="button"
+        accessibilityLabel={label}
+        accessibilityHint="">
+        <Text type="md-bold" style={pal.text}>
+          <FontAwesomeIcon
+            icon="angle-up"
+            size={21}
+            style={[pal.text, styles.icon]}
+          />
+        </Text>
+      </TouchableOpacity>
+    </>
   )
 }
 
@@ -67,13 +87,21 @@ const styles = StyleSheet.create({
     position: 'relative',
     top: 2,
   },
-  indicator: {
+  loadLatestCentered: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
     position: 'absolute',
-    top: 3,
-    right: 3,
-    backgroundColor: colors.blue3,
-    width: 10,
-    height: 10,
-    borderRadius: 6,
+    left: '50vw',
+    // @ts-ignore web only -prf
+    transform: 'translateX(-50%)',
+    top: 60,
+    paddingHorizontal: 24,
+    paddingVertical: 14,
+    borderRadius: 30,
+    borderWidth: 1,
+  },
+  loadLatestCenteredMinimal: {
+    top: 20,
   },
 })
diff --git a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
index 7d1823a13..412b9b803 100644
--- a/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
+++ b/src/view/com/util/load-latest/LoadLatestBtnMobile.tsx
@@ -6,7 +6,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {clamp} from 'lodash'
 import {useStores} from 'state/index'
 import {usePalette} from 'lib/hooks/usePalette'
-import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
 import {colors} from 'lib/styles'
 
 const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
@@ -20,6 +19,7 @@ export const LoadLatestBtn = observer(
     onPress: () => void
     label: string
     showIndicator: boolean
+    minimalShellMode?: boolean // NOTE not used on mobile -prf
   }) => {
     const store = useStores()
     const pal = usePalette('default')
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 2c03f1930..ab765e9cd 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -266,6 +266,7 @@ const FeedPage = observer(
             onPress={onPressLoadLatest}
             label="Load new posts"
             showIndicator={hasNew}
+            minimalShellMode={store.shell.minimalShellMode}
           />
         )}
         <FAB
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 64dcb3a1a..4db1d14ae 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -106,6 +106,7 @@ export const NotificationsScreen = withAuthRequired(
             onPress={onPressLoadLatest}
             label="Load new notifications"
             showIndicator={hasNew}
+            minimalShellMode={true}
           />
         )}
       </View>
-- 
cgit 1.4.1

