about summary refs log tree commit diff
path: root/src/view/com/algos
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/algos')
-rw-r--r--src/view/com/algos/AlgoItem.tsx153
-rw-r--r--src/view/com/algos/SavedFeedItem.tsx50
-rw-r--r--src/view/com/algos/useCustomFeed.ts27
3 files changed, 230 insertions, 0 deletions
diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx
new file mode 100644
index 000000000..56ee6d1d2
--- /dev/null
+++ b/src/view/com/algos/AlgoItem.tsx
@@ -0,0 +1,153 @@
+import React from 'react'
+import {
+  StyleProp,
+  StyleSheet,
+  View,
+  ViewStyle,
+  TouchableOpacity,
+} from 'react-native'
+import {Text} from '../util/text/Text'
+import {usePalette} from 'lib/hooks/usePalette'
+import {colors, s} from 'lib/styles'
+import {UserAvatar} from '../util/UserAvatar'
+import {Button} from '../util/forms/Button'
+import {observer} from 'mobx-react-lite'
+import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
+import {useFocusEffect, useNavigation} from '@react-navigation/native'
+import {NavigationProp} from 'lib/routes/types'
+import {useStores} from 'state/index'
+import {HeartIconSolid} from 'lib/icons'
+import {pluralize} from 'lib/strings/helpers'
+import {AtUri} from '@atproto/api'
+import {isWeb} from 'platform/detection'
+
+const AlgoItem = observer(
+  ({
+    item,
+    style,
+    showBottom = true,
+    reloadOnFocus = false,
+  }: {
+    item: AlgoItemModel
+    style?: StyleProp<ViewStyle>
+    showBottom?: boolean
+    reloadOnFocus?: boolean
+  }) => {
+    const store = useStores()
+    const pal = usePalette('default')
+    const navigation = useNavigation<NavigationProp>()
+
+    // TODO: this is pretty hacky, but it works for now
+    // causes issues on web
+    useFocusEffect(() => {
+      if (reloadOnFocus && !isWeb) {
+        item.reload()
+      }
+    })
+
+    return (
+      <TouchableOpacity
+        accessibilityRole="button"
+        style={[styles.container, style]}
+        onPress={() => {
+          navigation.navigate('CustomFeed', {
+            name: item.data.creator.did,
+            rkey: new AtUri(item.data.uri).rkey,
+            displayName:
+              item.data.displayName ??
+              `${item.data.creator.displayName}'s feed`,
+          })
+        }}
+        key={item.data.uri}>
+        <View style={[styles.headerContainer]}>
+          <View style={[s.mr10]}>
+            <UserAvatar size={36} avatar={item.data.avatar} />
+          </View>
+          <View style={[styles.headerTextContainer]}>
+            <Text style={[pal.text, s.bold]}>
+              {item.data.displayName ?? 'Feed name'}
+            </Text>
+            <Text style={[pal.textLight, styles.description]} numberOfLines={5}>
+              {item.data.description ??
+                "Explore our Feed for the latest updates and insights! Dive into a world of intriguing articles, trending news, and exciting stories that cover a wide range of topics. From technology breakthroughs to lifestyle tips, there's something here for everyone. Stay informed and get inspired with us. Join the conversation now!"}
+            </Text>
+          </View>
+        </View>
+
+        {showBottom ? (
+          <View style={styles.bottomContainer}>
+            <View style={styles.likedByContainer}>
+              {/* <View style={styles.likedByAvatars}>
+              <UserAvatar size={24} avatar={item.data.avatar} />
+              <UserAvatar size={24} avatar={item.data.avatar} />
+              <UserAvatar size={24} avatar={item.data.avatar} />
+            </View> */}
+
+              <HeartIconSolid size={16} style={[s.mr2, {color: colors.red3}]} />
+              <Text style={[pal.text, pal.textLight]}>
+                {item.data.likeCount && item.data.likeCount > 0
+                  ? `Liked by ${item.data.likeCount} ${pluralize(
+                      item.data.likeCount,
+                      'other',
+                    )}`
+                  : 'Be the first to like this'}
+              </Text>
+            </View>
+            <View>
+              <Button
+                type={item.isSaved ? 'default' : 'inverted'}
+                onPress={() => {
+                  if (item.data.viewer?.saved) {
+                    store.me.savedFeeds.unsave(item)
+                  } else {
+                    store.me.savedFeeds.save(item)
+                  }
+                }}
+                label={item.data.viewer?.saved ? 'Unsave' : 'Save'}
+              />
+            </View>
+          </View>
+        ) : null}
+      </TouchableOpacity>
+    )
+  },
+)
+export default AlgoItem
+
+const styles = StyleSheet.create({
+  container: {
+    paddingHorizontal: 18,
+    paddingVertical: 20,
+    flexDirection: 'column',
+    flex: 1,
+    borderTopWidth: 1,
+    borderTopColor: '#E5E5E5',
+    gap: 18,
+  },
+  headerContainer: {
+    flexDirection: 'row',
+  },
+  headerTextContainer: {
+    flexDirection: 'column',
+    columnGap: 4,
+    flex: 1,
+  },
+  description: {
+    flex: 1,
+    flexWrap: 'wrap',
+  },
+  bottomContainer: {
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    alignItems: 'center',
+  },
+  likedByContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 2,
+  },
+  likedByAvatars: {
+    flexDirection: 'row',
+    gap: -12,
+  },
+})
diff --git a/src/view/com/algos/SavedFeedItem.tsx b/src/view/com/algos/SavedFeedItem.tsx
new file mode 100644
index 000000000..bb4ec10b3
--- /dev/null
+++ b/src/view/com/algos/SavedFeedItem.tsx
@@ -0,0 +1,50 @@
+import React from 'react'
+import {View, TouchableOpacity, StyleSheet} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {colors} from 'lib/styles'
+import {observer} from 'mobx-react-lite'
+import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
+import {SavedFeedsModel} from 'state/models/feeds/algo/saved'
+import AlgoItem from './AlgoItem'
+
+export const SavedFeedItem = observer(
+  ({item, savedFeeds}: {item: AlgoItemModel; savedFeeds: SavedFeedsModel}) => {
+    const isPinned = savedFeeds.isPinned(item)
+
+    return (
+      <View style={styles.itemContainer}>
+        <AlgoItem
+          key={item.data.uri}
+          item={item}
+          showBottom={false}
+          style={styles.item}
+        />
+        <TouchableOpacity
+          accessibilityRole="button"
+          onPress={() => {
+            savedFeeds.togglePinnedFeed(item)
+            console.log('pinned', savedFeeds.pinned)
+            console.log('isPinned', savedFeeds.isPinned(item))
+          }}>
+          <FontAwesomeIcon
+            icon="thumb-tack"
+            size={20}
+            color={isPinned ? colors.blue3 : colors.gray3}
+          />
+        </TouchableOpacity>
+      </View>
+    )
+  },
+)
+
+const styles = StyleSheet.create({
+  itemContainer: {
+    flex: 1,
+    flexDirection: 'row',
+    alignItems: 'center',
+    marginRight: 18,
+  },
+  item: {
+    borderTopWidth: 0,
+  },
+})
diff --git a/src/view/com/algos/useCustomFeed.ts b/src/view/com/algos/useCustomFeed.ts
new file mode 100644
index 000000000..cea9c1cea
--- /dev/null
+++ b/src/view/com/algos/useCustomFeed.ts
@@ -0,0 +1,27 @@
+import {useEffect, useState} from 'react'
+import {useStores} from 'state/index'
+import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
+
+export function useCustomFeed(uri: string) {
+  const store = useStores()
+  const [item, setItem] = useState<AlgoItemModel>()
+  useEffect(() => {
+    async function fetchView() {
+      const res = await store.agent.app.bsky.feed.getFeedGenerator({
+        feed: uri,
+      })
+      const view = res.data.view
+      return view
+    }
+    async function buildFeedItem() {
+      const view = await fetchView()
+      if (view) {
+        const temp = new AlgoItemModel(store, view)
+        setItem(temp)
+      }
+    }
+    buildFeedItem()
+  }, [store, uri])
+
+  return item
+}