about summary refs log tree commit diff
path: root/src/view/com/lists
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-11-12 12:45:25 -0800
committerGitHub <noreply@github.com>2023-11-12 12:45:25 -0800
commitd9e0a927c1c98ebd6aa3885ab517af27e7de2522 (patch)
treecee196297391e497f1aa3b650d66633f3a86ca34 /src/view/com/lists
parent05b728fffcdb17708fdb52685725faf7fdc545bc (diff)
downloadvoidsky-d9e0a927c1c98ebd6aa3885ab517af27e7de2522.tar.zst
Refactor lists to use new queries (#1875)
* Refactor lists queries to react-query

* Delete old lists-list model

* Implement list, list-members, and list-memberships react-queries

* Update CreateOrEditList modal

* First pass at my-follows and actor-autocomplete queries

* Update ListAddUserModal to use new queries, change to ListAddRemoveUsersModal

* Update UserAddRemoveLists modal

* Remove old TODO

* Fix indent, autocomplete query

* Add a todo

---------

Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src/view/com/lists')
-rw-r--r--src/view/com/lists/ListCard.tsx6
-rw-r--r--src/view/com/lists/ListMembers.tsx (renamed from src/view/com/lists/ListItems.tsx)94
-rw-r--r--src/view/com/lists/ListsList.tsx70
3 files changed, 74 insertions, 96 deletions
diff --git a/src/view/com/lists/ListCard.tsx b/src/view/com/lists/ListCard.tsx
index a481902d8..774e9e916 100644
--- a/src/view/com/lists/ListCard.tsx
+++ b/src/view/com/lists/ListCard.tsx
@@ -7,7 +7,7 @@ import {RichText as RichTextCom} from '../util/text/RichText'
 import {UserAvatar} from '../util/UserAvatar'
 import {s} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
-import {useStores} from 'state/index'
+import {useSession} from '#/state/session'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {sanitizeHandle} from 'lib/strings/handles'
 import {makeProfileLink} from 'lib/routes/links'
@@ -28,7 +28,7 @@ export const ListCard = ({
   style?: StyleProp<ViewStyle>
 }) => {
   const pal = usePalette('default')
-  const store = useStores()
+  const {currentAccount} = useSession()
 
   const rkey = React.useMemo(() => {
     try {
@@ -80,7 +80,7 @@ export const ListCard = ({
             {list.purpose === 'app.bsky.graph.defs#modlist' &&
               'Moderation list '}
             by{' '}
-            {list.creator.did === store.me.did
+            {list.creator.did === currentAccount?.did
               ? 'you'
               : sanitizeHandle(list.creator.handle, '@')}
           </Text>
diff --git a/src/view/com/lists/ListItems.tsx b/src/view/com/lists/ListMembers.tsx
index cf6fd3b42..4a25c53e6 100644
--- a/src/view/com/lists/ListItems.tsx
+++ b/src/view/com/lists/ListMembers.tsx
@@ -9,27 +9,28 @@ import {
 } from 'react-native'
 import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
 import {FlatList} from '../util/Views'
-import {observer} from 'mobx-react-lite'
 import {ProfileCardFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
 import {ProfileCard} from '../profile/ProfileCard'
 import {Button} from '../util/forms/Button'
-import {ListModel} from 'state/models/content/list'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
+import {useListMembersQuery} from '#/state/queries/list-members'
 import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
 import {logger} from '#/logger'
 import {useModalControls} from '#/state/modals'
 import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
+import {useSession} from '#/state/session'
+import {cleanError} from '#/lib/strings/errors'
 
 const LOADING_ITEM = {_reactKey: '__loading__'}
 const EMPTY_ITEM = {_reactKey: '__empty__'}
 const ERROR_ITEM = {_reactKey: '__error__'}
 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
 
-export const ListItems = observer(function ListItemsImpl({
+export function ListMembers({
   list,
   style,
   scrollElRef,
@@ -42,7 +43,7 @@ export const ListItems = observer(function ListItemsImpl({
   headerOffset = 0,
   desktopFixedHeightOffset,
 }: {
-  list: ListModel
+  list: string
   style?: StyleProp<ViewStyle>
   scrollElRef?: MutableRefObject<FlatList<any> | null>
   onScroll: OnScrollHandler
@@ -59,33 +60,43 @@ export const ListItems = observer(function ListItemsImpl({
   const [isRefreshing, setIsRefreshing] = React.useState(false)
   const {isMobile} = useWebMediaQueries()
   const {openModal} = useModalControls()
+  const {currentAccount} = useSession()
 
-  const data = React.useMemo(() => {
+  const {
+    data,
+    isFetching,
+    isFetched,
+    isError,
+    error,
+    refetch,
+    fetchNextPage,
+    hasNextPage,
+  } = useListMembersQuery(list)
+  const isEmpty = !isFetching && !data?.pages[0].items.length
+  const isOwner =
+    currentAccount && data?.pages[0].list.creator.did === currentAccount.did
+
+  const items = React.useMemo(() => {
     let items: any[] = []
-    if (list.hasLoaded) {
-      if (list.hasError) {
+    if (isFetched) {
+      if (isEmpty && isError) {
         items = items.concat([ERROR_ITEM])
       }
-      if (list.isEmpty) {
+      if (isEmpty) {
         items = items.concat([EMPTY_ITEM])
-      } else {
-        items = items.concat(list.items)
+      } else if (data) {
+        for (const page of data.pages) {
+          items = items.concat(page.items)
+        }
       }
-      if (list.loadMoreError) {
+      if (!isEmpty && isError) {
         items = items.concat([LOAD_MORE_ERROR_ITEM])
       }
-    } else if (list.isLoading) {
+    } else if (isFetching) {
       items = items.concat([LOADING_ITEM])
     }
     return items
-  }, [
-    list.hasError,
-    list.hasLoaded,
-    list.isLoading,
-    list.isEmpty,
-    list.items,
-    list.loadMoreError,
-  ])
+  }, [isFetched, isEmpty, isError, data, isFetching])
 
   // events
   // =
@@ -94,25 +105,26 @@ export const ListItems = observer(function ListItemsImpl({
     track('Lists:onRefresh')
     setIsRefreshing(true)
     try {
-      await list.refresh()
+      await refetch()
     } catch (err) {
       logger.error('Failed to refresh lists', {error: err})
     }
     setIsRefreshing(false)
-  }, [list, track, setIsRefreshing])
+  }, [refetch, track, setIsRefreshing])
 
   const onEndReached = React.useCallback(async () => {
+    if (isFetching || !hasNextPage || isError) return
     track('Lists:onEndReached')
     try {
-      await list.loadMore()
+      await fetchNextPage()
     } catch (err) {
       logger.error('Failed to load more lists', {error: err})
     }
-  }, [list, track])
+  }, [isFetching, hasNextPage, isError, fetchNextPage, track])
 
   const onPressRetryLoadMore = React.useCallback(() => {
-    list.retryLoadMore()
-  }, [list])
+    fetchNextPage()
+  }, [fetchNextPage])
 
   const onPressEditMembership = React.useCallback(
     (profile: AppBskyActorDefs.ProfileViewBasic) => {
@@ -120,19 +132,9 @@ export const ListItems = observer(function ListItemsImpl({
         name: 'user-add-remove-lists',
         subject: profile.did,
         displayName: profile.displayName || profile.handle,
-        onAdd(listUri: string) {
-          if (listUri === list.uri) {
-            list.cacheAddMember(profile)
-          }
-        },
-        onRemove(listUri: string) {
-          if (listUri === list.uri) {
-            list.cacheRemoveMember(profile)
-          }
-        },
       })
     },
-    [openModal, list],
+    [openModal],
   )
 
   // rendering
@@ -140,7 +142,7 @@ export const ListItems = observer(function ListItemsImpl({
 
   const renderMemberButton = React.useCallback(
     (profile: AppBskyActorDefs.ProfileViewBasic) => {
-      if (!list.isOwner) {
+      if (!isOwner) {
         return null
       }
       return (
@@ -152,7 +154,7 @@ export const ListItems = observer(function ListItemsImpl({
         />
       )
     },
-    [list, onPressEditMembership],
+    [isOwner, onPressEditMembership],
   )
 
   const renderItem = React.useCallback(
@@ -162,7 +164,7 @@ export const ListItems = observer(function ListItemsImpl({
       } else if (item === ERROR_ITEM) {
         return (
           <ErrorMessage
-            message={list.error}
+            message={cleanError(error)}
             onPressTryAgain={onPressTryAgain}
           />
         )
@@ -190,7 +192,7 @@ export const ListItems = observer(function ListItemsImpl({
     [
       renderMemberButton,
       renderEmptyState,
-      list.error,
+      error,
       onPressTryAgain,
       onPressRetryLoadMore,
       isMobile,
@@ -200,10 +202,10 @@ export const ListItems = observer(function ListItemsImpl({
   const Footer = React.useCallback(
     () => (
       <View style={{paddingTop: 20, paddingBottom: 200}}>
-        {list.isLoading && <ActivityIndicator />}
+        {isFetching && <ActivityIndicator />}
       </View>
     ),
-    [list.isLoading],
+    [isFetching],
   )
 
   const scrollHandler = useAnimatedScrollHandler(onScroll)
@@ -212,8 +214,8 @@ export const ListItems = observer(function ListItemsImpl({
       <FlatList
         testID={testID ? `${testID}-flatlist` : undefined}
         ref={scrollElRef}
-        data={data}
-        keyExtractor={(item: any) => item._reactKey}
+        data={items}
+        keyExtractor={(item: any) => item.uri || item._reactKey}
         renderItem={renderItem}
         ListHeaderComponent={renderHeader}
         ListFooterComponent={Footer}
@@ -241,4 +243,4 @@ export const ListItems = observer(function ListItemsImpl({
       />
     </View>
   )
-})
+}
diff --git a/src/view/com/lists/ListsList.tsx b/src/view/com/lists/ListsList.tsx
index 2883a31d5..100e0d609 100644
--- a/src/view/com/lists/ListsList.tsx
+++ b/src/view/com/lists/ListsList.tsx
@@ -8,68 +8,59 @@ import {
   View,
   ViewStyle,
 } from 'react-native'
-import {observer} from 'mobx-react-lite'
 import {AppBskyGraphDefs as GraphDefs} from '@atproto/api'
 import {ListCard} from './ListCard'
+import {MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
 import {Text} from '../util/text/Text'
-import {ListsListModel} from 'state/models/lists/lists-list'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {usePalette} from 'lib/hooks/usePalette'
 import {FlatList} from '../util/Views'
 import {s} from 'lib/styles'
 import {logger} from '#/logger'
 import {Trans} from '@lingui/macro'
+import {cleanError} from '#/lib/strings/errors'
 
 const LOADING = {_reactKey: '__loading__'}
 const EMPTY = {_reactKey: '__empty__'}
 const ERROR_ITEM = {_reactKey: '__error__'}
 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
 
-export const ListsList = observer(function ListsListImpl({
-  listsList,
+export function ListsList({
+  filter,
   inline,
   style,
-  onPressTryAgain,
   renderItem,
   testID,
 }: {
-  listsList: ListsListModel
+  filter: MyListsFilter
   inline?: boolean
   style?: StyleProp<ViewStyle>
-  onPressTryAgain?: () => void
   renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element
   testID?: string
 }) {
   const pal = usePalette('default')
   const {track} = useAnalytics()
   const [isRefreshing, setIsRefreshing] = React.useState(false)
+  const {data, isFetching, isFetched, isError, error, refetch} =
+    useMyListsQuery(filter)
+  const isEmpty = !isFetching && !data?.length
 
-  const data = React.useMemo(() => {
+  const items = React.useMemo(() => {
     let items: any[] = []
-    if (listsList.hasError) {
+    if (isError && isEmpty) {
       items = items.concat([ERROR_ITEM])
     }
-    if (!listsList.hasLoaded && listsList.isLoading) {
+    if (!isFetched && isFetching) {
       items = items.concat([LOADING])
-    } else if (listsList.isEmpty) {
+    } else if (isEmpty) {
       items = items.concat([EMPTY])
     } else {
-      items = items.concat(listsList.lists)
-    }
-    if (listsList.loadMoreError) {
-      items = items.concat([LOAD_MORE_ERROR_ITEM])
+      items = items.concat(data)
     }
     return items
-  }, [
-    listsList.hasError,
-    listsList.hasLoaded,
-    listsList.isLoading,
-    listsList.lists,
-    listsList.isEmpty,
-    listsList.loadMoreError,
-  ])
+  }, [isError, isEmpty, isFetched, isFetching, data])
 
   // events
   // =
@@ -78,25 +69,12 @@ export const ListsList = observer(function ListsListImpl({
     track('Lists:onRefresh')
     setIsRefreshing(true)
     try {
-      await listsList.refresh()
+      await refetch()
     } catch (err) {
       logger.error('Failed to refresh lists', {error: err})
     }
     setIsRefreshing(false)
-  }, [listsList, track, setIsRefreshing])
-
-  const onEndReached = React.useCallback(async () => {
-    track('Lists:onEndReached')
-    try {
-      await listsList.loadMore()
-    } catch (err) {
-      logger.error('Failed to load more lists', {error: err})
-    }
-  }, [listsList, track])
-
-  const onPressRetryLoadMore = React.useCallback(() => {
-    listsList.retryLoadMore()
-  }, [listsList])
+  }, [refetch, track, setIsRefreshing])
 
   // rendering
   // =
@@ -116,15 +94,15 @@ export const ListsList = observer(function ListsListImpl({
       } else if (item === ERROR_ITEM) {
         return (
           <ErrorMessage
-            message={listsList.error}
-            onPressTryAgain={onPressTryAgain}
+            message={cleanError(error)}
+            onPressTryAgain={onRefresh}
           />
         )
       } else if (item === LOAD_MORE_ERROR_ITEM) {
         return (
           <LoadMoreRetryBtn
             label="There was an issue fetching your lists. Tap here to try again."
-            onPress={onPressRetryLoadMore}
+            onPress={onRefresh}
           />
         )
       } else if (item === LOADING) {
@@ -144,16 +122,16 @@ export const ListsList = observer(function ListsListImpl({
         />
       )
     },
-    [listsList, onPressTryAgain, onPressRetryLoadMore, renderItem, pal],
+    [error, onRefresh, renderItem, pal],
   )
 
   const FlatListCom = inline ? RNFlatList : FlatList
   return (
     <View testID={testID} style={style}>
-      {data.length > 0 && (
+      {items.length > 0 && (
         <FlatListCom
           testID={testID ? `${testID}-flatlist` : undefined}
-          data={data}
+          data={items}
           keyExtractor={(item: any) => item._reactKey}
           renderItem={renderItemInner}
           refreshControl={
@@ -165,8 +143,6 @@ export const ListsList = observer(function ListsListImpl({
             />
           }
           contentContainerStyle={[s.contentContainer]}
-          onEndReached={onEndReached}
-          onEndReachedThreshold={0.6}
           removeClippedSubviews={true}
           // @ts-ignore our .web version only -prf
           desktopFixedHeight
@@ -174,7 +150,7 @@ export const ListsList = observer(function ListsListImpl({
       )}
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   item: {