about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2023-08-10 12:50:37 -0500
committerGitHub <noreply@github.com>2023-08-10 10:50:37 -0700
commitcc3fcb1645060efb8765606d277b91ebb3303ae4 (patch)
tree7ea0bd21ee8e4ac4f26583687dffca328c41e634
parent03d152675ee1ce208856498acf7285fbf07fd45b (diff)
downloadvoidsky-cc3fcb1645060efb8765606d277b91ebb3303ae4.tar.zst
Adds profile media tab (#1137)
* add media tab

* fix loading state

* cleanup

* update naming

* upgrade api package

* fix load state

* add scroll view to tabs

* fix overflow on mobile web
-rw-r--r--package.json2
-rw-r--r--src/state/models/feeds/posts.ts18
-rw-r--r--src/state/models/ui/profile.ts54
-rw-r--r--src/view/com/util/ViewSelector.tsx92
-rw-r--r--yarn.lock8
5 files changed, 109 insertions, 65 deletions
diff --git a/package.json b/package.json
index 64e7a4e66..0f7e17d53 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
     "e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all"
   },
   "dependencies": {
-    "@atproto/api": "^0.6.0",
+    "@atproto/api": "^0.6.1",
     "@bam.tech/react-native-image-resizer": "^3.0.4",
     "@braintree/sanitize-url": "^6.0.2",
     "@expo/html-elements": "^0.4.2",
diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts
index bc1227fd6..52717953b 100644
--- a/src/state/models/feeds/posts.ts
+++ b/src/state/models/feeds/posts.ts
@@ -74,24 +74,6 @@ export class PostsFeedModel {
     return this.hasLoaded && !this.hasContent
   }
 
-  get nonReplyFeed() {
-    if (this.feedType === 'author') {
-      return this.slices.filter(slice => {
-        const params = this.params as GetAuthorFeed.QueryParams
-        const item = slice.rootItem
-        const isRepost =
-          item?.reasonRepost?.by?.handle === params.actor ||
-          item?.reasonRepost?.by?.did === params.actor
-        const allow =
-          !item.postRecord?.reply || // not a reply
-          isRepost // but allow if it's a repost
-        return allow
-      })
-    } else {
-      return this.slices
-    }
-  }
-
   setHasNewLatest(v: boolean) {
     this.hasNewLatest = v
   }
diff --git a/src/state/models/ui/profile.ts b/src/state/models/ui/profile.ts
index a0249d768..a8c8ec0a0 100644
--- a/src/state/models/ui/profile.ts
+++ b/src/state/models/ui/profile.ts
@@ -6,8 +6,9 @@ import {ActorFeedsModel} from '../lists/actor-feeds'
 import {ListsListModel} from '../lists/lists-list'
 
 export enum Sections {
-  Posts = 'Posts',
+  PostsNoReplies = 'Posts',
   PostsWithReplies = 'Posts & replies',
+  PostsWithMedia = 'Media',
   CustomAlgorithms = 'Feeds',
   Lists = 'Lists',
 }
@@ -46,6 +47,7 @@ export class ProfileUiModel {
     this.feed = new PostsFeedModel(rootStore, 'author', {
       actor: params.user,
       limit: 10,
+      filter: 'posts_no_replies',
     })
     this.algos = new ActorFeedsModel(rootStore, {actor: params.user})
     this.lists = new ListsListModel(rootStore, params.user)
@@ -53,8 +55,9 @@ export class ProfileUiModel {
 
   get currentView(): PostsFeedModel | ActorFeedsModel | ListsListModel {
     if (
-      this.selectedView === Sections.Posts ||
-      this.selectedView === Sections.PostsWithReplies
+      this.selectedView === Sections.PostsNoReplies ||
+      this.selectedView === Sections.PostsWithReplies ||
+      this.selectedView === Sections.PostsWithMedia
     ) {
       return this.feed
     } else if (this.selectedView === Sections.Lists) {
@@ -76,7 +79,11 @@ export class ProfileUiModel {
   }
 
   get selectorItems() {
-    const items = [Sections.Posts, Sections.PostsWithReplies]
+    const items = [
+      Sections.PostsNoReplies,
+      Sections.PostsWithReplies,
+      Sections.PostsWithMedia,
+    ]
     if (this.algos.hasLoaded && !this.algos.isEmpty) {
       items.push(Sections.CustomAlgorithms)
     }
@@ -90,7 +97,7 @@ export class ProfileUiModel {
     // If, for whatever reason, the selected view index is not available, default back to posts
     // This can happen when the user was focused on a view but performed an action that caused
     // the view to disappear (e.g. deleting the last list in their list of lists https://imgflip.com/i/7txu1y)
-    return this.selectorItems[this.selectedViewIndex] || Sections.Posts
+    return this.selectorItems[this.selectedViewIndex] || Sections.PostsNoReplies
   }
 
   get uiItems() {
@@ -109,16 +116,22 @@ export class ProfileUiModel {
     } else {
       // not loading, no error, show content
       if (
-        this.selectedView === Sections.Posts ||
+        this.selectedView === Sections.PostsNoReplies ||
         this.selectedView === Sections.PostsWithReplies ||
+        this.selectedView === Sections.PostsWithMedia ||
         this.selectedView === Sections.CustomAlgorithms
       ) {
         if (this.feed.hasContent) {
           if (this.selectedView === Sections.CustomAlgorithms) {
             arr = this.algos.feeds
-          } else if (this.selectedView === Sections.Posts) {
-            arr = this.feed.nonReplyFeed
+          } else if (
+            this.selectedView === Sections.PostsNoReplies ||
+            this.selectedView === Sections.PostsWithReplies ||
+            this.selectedView === Sections.PostsWithMedia
+          ) {
+            arr = this.feed.slices.slice()
           } else {
+            // posts with replies is also default
             arr = this.feed.slices.slice()
           }
           if (!this.feed.hasMore) {
@@ -143,8 +156,9 @@ export class ProfileUiModel {
 
   get showLoadingMoreFooter() {
     if (
-      this.selectedView === Sections.Posts ||
-      this.selectedView === Sections.PostsWithReplies
+      this.selectedView === Sections.PostsNoReplies ||
+      this.selectedView === Sections.PostsWithReplies ||
+      this.selectedView === Sections.PostsWithMedia
     ) {
       return this.feed.hasContent && this.feed.hasMore && this.feed.isLoading
     } else if (this.selectedView === Sections.Lists) {
@@ -157,7 +171,27 @@ export class ProfileUiModel {
   // =
 
   setSelectedViewIndex(index: number) {
+    // ViewSelector fires onSelectView on mount
+    if (index === this.selectedViewIndex) return
+
     this.selectedViewIndex = index
+
+    let filter = 'posts_no_replies'
+    if (this.selectedView === Sections.PostsWithReplies) {
+      filter = 'posts_with_replies'
+    } else if (this.selectedView === Sections.PostsWithMedia) {
+      filter = 'posts_with_media'
+    }
+
+    this.feed = new PostsFeedModel(this.rootStore, 'author', {
+      actor: this.params.user,
+      limit: 10,
+      filter,
+    })
+
+    if (this.currentView instanceof PostsFeedModel) {
+      this.feed.setup()
+    }
   }
 
   async setup() {
diff --git a/src/view/com/util/ViewSelector.tsx b/src/view/com/util/ViewSelector.tsx
index e2f47ba89..c2912ba45 100644
--- a/src/view/com/util/ViewSelector.tsx
+++ b/src/view/com/util/ViewSelector.tsx
@@ -1,5 +1,11 @@
 import React, {useEffect, useState} from 'react'
-import {Pressable, RefreshControl, StyleSheet, View} from 'react-native'
+import {
+  Pressable,
+  RefreshControl,
+  StyleSheet,
+  View,
+  ScrollView,
+} from 'react-native'
 import {FlatList} from './Views'
 import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
 import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
@@ -131,6 +137,8 @@ export const ViewSelector = React.forwardRef<
   },
 )
 
+const SCROLLBAR_OFFSET = 12
+
 export function Selector({
   selectedIndex,
   items,
@@ -140,6 +148,8 @@ export function Selector({
   items: string[]
   onSelect?: (index: number) => void
 }) {
+  const [height, setHeight] = useState(0)
+
   const pal = usePalette('default')
   const borderColor = useColorSchemeStyle(
     {borderColor: colors.black},
@@ -151,37 +161,55 @@ export function Selector({
   }
 
   return (
-    <View style={[pal.view, styles.outer]}>
-      {items.map((item, i) => {
-        const selected = i === selectedIndex
-        return (
-          <Pressable
-            testID={`selector-${i}`}
-            key={item}
-            onPress={() => onPressItem(i)}
-            accessibilityLabel={item}
-            accessibilityHint={`Selects ${item}`}
-            // TODO: Modify the component API such that lint fails
-            // at the invocation site as well
-          >
-            <View
-              style={[
-                styles.item,
-                selected && styles.itemSelected,
-                borderColor,
-              ]}>
-              <Text
-                style={
-                  selected
-                    ? [styles.labelSelected, pal.text]
-                    : [styles.label, pal.textLight]
-                }>
-                {item}
-              </Text>
-            </View>
-          </Pressable>
-        )
-      })}
+    <View
+      style={{
+        width: '100%',
+        position: 'relative',
+        overflow: 'hidden',
+        marginTop: -SCROLLBAR_OFFSET,
+        height,
+      }}>
+      <ScrollView
+        horizontal
+        style={{position: 'absolute', bottom: -SCROLLBAR_OFFSET}}>
+        <View
+          style={[pal.view, styles.outer, {paddingBottom: SCROLLBAR_OFFSET}]}
+          onLayout={e => {
+            const {height} = e.nativeEvent.layout
+            setHeight(height || 60)
+          }}>
+          {items.map((item, i) => {
+            const selected = i === selectedIndex
+            return (
+              <Pressable
+                testID={`selector-${i}`}
+                key={item}
+                onPress={() => onPressItem(i)}
+                accessibilityLabel={item}
+                accessibilityHint={`Selects ${item}`}
+                // TODO: Modify the component API such that lint fails
+                // at the invocation site as well
+              >
+                <View
+                  style={[
+                    styles.item,
+                    selected && styles.itemSelected,
+                    borderColor,
+                  ]}>
+                  <Text
+                    style={
+                      selected
+                        ? [styles.labelSelected, pal.text]
+                        : [styles.label, pal.textLight]
+                    }>
+                    {item}
+                  </Text>
+                </View>
+              </Pressable>
+            )
+          })}
+        </View>
+      </ScrollView>
     </View>
   )
 }
diff --git a/yarn.lock b/yarn.lock
index 6e13bd51e..c418e9acb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -40,10 +40,10 @@
     tlds "^1.234.0"
     typed-emitter "^2.1.0"
 
-"@atproto/api@^0.6.0":
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.6.0.tgz#c4eea08ee4d1be522928cd016d7de8061d86e573"
-  integrity sha512-GkWHoGZfNneHarAYkIPJD1GGgKiI7OwnCtKS+J4AmlVKYijGEzOYgg1fY6rluT6XPT5TlQZiHUWpMlpqAkQIkQ==
+"@atproto/api@^0.6.1":
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.6.1.tgz#1a4794c4e379f3790dbc1c2cc69e0700c711f634"
+  integrity sha512-Fwp3GxSxy04XCScLNb7gdYuITt3beUPM2gOmAaJJ/c0muvj3BS/lGeeEqHToSMlxyirfPQYiTHDGcDZgo6EpMQ==
   dependencies:
     "@atproto/common-web" "*"
     "@atproto/uri" "*"