about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/state/models/feeds/algo/saved.ts30
-rw-r--r--src/view/com/algos/AlgoItem.tsx4
-rw-r--r--src/view/com/pager/FeedsTabBarMobile.tsx13
-rw-r--r--src/view/com/pager/TabBar.tsx132
4 files changed, 111 insertions, 68 deletions
diff --git a/src/state/models/feeds/algo/saved.ts b/src/state/models/feeds/algo/saved.ts
index bc36aaed8..5d2f854dc 100644
--- a/src/state/models/feeds/algo/saved.ts
+++ b/src/state/models/feeds/algo/saved.ts
@@ -89,6 +89,36 @@ export class SavedFeedsModel {
     }
   })
 
+  removeFeed(uri: string) {
+    this.feeds = this.feeds.filter(f => f.data.uri !== uri)
+  }
+
+  addFeed(algoItem: AlgoItemModel) {
+    this.feeds.push(new AlgoItemModel(this.rootStore, algoItem.data))
+  }
+
+  async save(algoItem: AlgoItemModel) {
+    try {
+      await this.rootStore.agent.app.bsky.feed.saveFeed({
+        feed: algoItem.getUri,
+      })
+      this.addFeed(algoItem)
+    } catch (e: any) {
+      this.rootStore.log.error('Failed to save feed', e)
+    }
+  }
+
+  async unsave(uri: string) {
+    try {
+      await this.rootStore.agent.app.bsky.feed.unsaveFeed({
+        feed: uri,
+      })
+      this.removeFeed(uri)
+    } catch (e: any) {
+      this.rootStore.log.error('Failed to unsanve feed', e)
+    }
+  }
+
   // state transitions
   // =
 
diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx
index 51de89bd6..04117e589 100644
--- a/src/view/com/algos/AlgoItem.tsx
+++ b/src/view/com/algos/AlgoItem.tsx
@@ -15,9 +15,11 @@ import {observer} from 'mobx-react-lite'
 import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
 import {useNavigation} from '@react-navigation/native'
 import {NavigationProp} from 'lib/routes/types'
+import {useStores} from 'state/index'
 
 const AlgoItem = observer(
   ({item, style}: {item: AlgoItemModel; style?: StyleProp<ViewStyle>}) => {
+    const store = useStores()
     const pal = usePalette('default')
     const navigation = useNavigation<NavigationProp>()
 
@@ -64,8 +66,10 @@ const AlgoItem = observer(
               onPress={() => {
                 if (item.data.viewer?.saved) {
                   item.unsave()
+                  store.me.savedFeeds.removeFeed(item.data.uri)
                 } else {
                   item.save()
+                  store.me.savedFeeds.addFeed(item)
                 }
               }}
               label={item.data.viewer?.saved ? 'Unsave' : 'Save'}
diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx
index c3c442552..3d1ed2c10 100644
--- a/src/view/com/pager/FeedsTabBarMobile.tsx
+++ b/src/view/com/pager/FeedsTabBarMobile.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, {useMemo} from 'react'
 import {Animated, StyleSheet, TouchableOpacity} from 'react-native'
 import {observer} from 'mobx-react-lite'
 import {TabBar} from 'view/com/pager/TabBar'
@@ -32,6 +32,11 @@ export const FeedsTabBar = observer(
       store.shell.openDrawer()
     }, [store])
 
+    const items = useMemo(
+      () => ['Following', "What's hot", ...store.me.savedFeeds.listOfFeedNames],
+      [store.me.savedFeeds.listOfFeedNames],
+    )
+
     return (
       <Animated.View style={[pal.view, pal.border, styles.tabBar, transform]}>
         <TouchableOpacity
@@ -45,11 +50,7 @@ export const FeedsTabBar = observer(
         </TouchableOpacity>
         <TabBar
           {...props}
-          items={[
-            'Following',
-            "What's hot",
-            ...store.me.savedFeeds.listOfFeedNames,
-          ]}
+          items={items}
           indicatorPosition="bottom"
           indicatorColor={pal.colors.link}
         />
diff --git a/src/view/com/pager/TabBar.tsx b/src/view/com/pager/TabBar.tsx
index 5d2e18e3e..3a7a5583e 100644
--- a/src/view/com/pager/TabBar.tsx
+++ b/src/view/com/pager/TabBar.tsx
@@ -35,59 +35,59 @@ export function TabBar({
   onPressSelected,
 }: TabBarProps) {
   const pal = usePalette('default')
-  const [itemLayouts, setItemLayouts] = useState<Layout[]>(
-    items.map(() => ({x: 0, width: 0})),
-  )
+  // const [itemLayouts, setItemLayouts] = useState<Layout[]>(
+  //   items.map(() => ({x: 0, width: 0})),
+  // )
   const itemRefs = useMemo(
     () => Array.from({length: items.length}).map(() => createRef<View>()),
     [items.length],
   )
-  const panX = Animated.add(position, offset)
+  // const panX = Animated.add(position, offset)
   const containerRef = useRef<View>(null)
 
-  const indicatorStyle = {
-    backgroundColor: indicatorColor || pal.colors.link,
-    bottom:
-      indicatorPosition === 'bottom' ? (isDesktopWeb ? 0 : -1) : undefined,
-    top: indicatorPosition === 'top' ? (isDesktopWeb ? 0 : -1) : undefined,
-    transform: [
-      {
-        translateX: panX.interpolate({
-          inputRange: items.map((_item, i) => i),
-          outputRange: itemLayouts.map(l => l.x + l.width / 2),
-        }),
-      },
-      {
-        scaleX: panX.interpolate({
-          inputRange: items.map((_item, i) => i),
-          outputRange: itemLayouts.map(l => l.width),
-        }),
-      },
-    ],
-  }
+  // const indicatorStyle = {
+  //   backgroundColor: indicatorColor || pal.colors.link,
+  //   bottom:
+  //     indicatorPosition === 'bottom' ? (isDesktopWeb ? 0 : -1) : undefined,
+  //   top: indicatorPosition === 'top' ? (isDesktopWeb ? 0 : -1) : undefined,
+  //   transform: [
+  //     {
+  //       translateX: panX.interpolate({
+  //         inputRange: items.map((_item, i) => i),
+  //         outputRange: itemLayouts.map(l => l.x + l.width / 2),
+  //       }),
+  //     },
+  //     {
+  //       scaleX: panX.interpolate({
+  //         inputRange: items.map((_item, i) => i),
+  //         outputRange: itemLayouts.map(l => l.width),
+  //       }),
+  //     },
+  //   ],
+  // }
 
-  const onLayout = () => {
-    const promises = []
-    for (let i = 0; i < items.length; i++) {
-      promises.push(
-        new Promise<Layout>(resolve => {
-          if (!containerRef.current || !itemRefs[i].current) {
-            return resolve({x: 0, width: 0})
-          }
+  // const onLayout = () => {
+  //   const promises = []
+  //   for (let i = 0; i < items.length; i++) {
+  //     promises.push(
+  //       new Promise<Layout>(resolve => {
+  //         if (!containerRef.current || !itemRefs[i].current) {
+  //           return resolve({x: 0, width: 0})
+  //         }
 
-          itemRefs[i].current?.measureLayout(
-            containerRef.current,
-            (x: number, _y: number, width: number) => {
-              resolve({x, width})
-            },
-          )
-        }),
-      )
-    }
-    Promise.all(promises).then((layouts: Layout[]) => {
-      setItemLayouts(layouts)
-    })
-  }
+  //         itemRefs[i].current?.measureLayout(
+  //           containerRef.current,
+  //           (x: number, _y: number, width: number) => {
+  //             resolve({x, width})
+  //           },
+  //         )
+  //       }),
+  //     )
+  //   }
+  //   Promise.all(promises).then((layouts: Layout[]) => {
+  //     setItemLayouts(layouts)
+  //   })
+  // }
 
   const onPressItem = (index: number) => {
     onSelect?.(index)
@@ -100,28 +100,31 @@ export function TabBar({
     <View
       testID={testID}
       style={[pal.view, styles.outer]}
-      onLayout={onLayout}
+      // onLayout={onLayout}
       ref={containerRef}>
-      <Animated.View style={[styles.indicator, indicatorStyle]} />
+      <Animated.View style={[styles.indicator]} />
       <ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
         {items.map((item, i) => {
           const selected = i === selectedPage
           return (
-            <PressableWithHover
-              ref={itemRefs[i]}
-              key={item}
-              style={
-                indicatorPosition === 'top' ? styles.itemTop : styles.itemBottom
-              }
-              hoverStyle={pal.viewLight}
-              onPress={() => onPressItem(i)}>
-              <Text
-                type="xl-bold"
-                testID={testID ? `${testID}-${item}` : undefined}
-                style={selected ? pal.text : pal.textLight}>
-                {item}
-              </Text>
-            </PressableWithHover>
+            <Animated.View key={item} style={selected ? styles.active : []}>
+              <PressableWithHover
+                ref={itemRefs[i]}
+                style={[
+                  indicatorPosition === 'top'
+                    ? styles.itemTop
+                    : styles.itemBottom,
+                ]}
+                hoverStyle={pal.viewLight}
+                onPress={() => onPressItem(i)}>
+                <Text
+                  type="xl-bold"
+                  testID={testID ? `${testID}-${item}` : undefined}
+                  style={selected ? pal.text : pal.textLight}>
+                  {item}
+                </Text>
+              </PressableWithHover>
+            </Animated.View>
           )
         })}
       </ScrollView>
@@ -152,6 +155,7 @@ const styles = isDesktopWeb
         height: 3,
         zIndex: 1,
       },
+      active: {},
     })
   : StyleSheet.create({
       outer: {
@@ -174,4 +178,8 @@ const styles = isDesktopWeb
         width: 1,
         height: 3,
       },
+      active: {
+        borderBottomColor: 'blue',
+        borderBottomWidth: 3,
+      },
     })