Post PostLikedBy and PostRepostedBy to RQ (#1913)
* Port PostRepostedBy to RQ * Port PostLikedBy to RQzio/stable
parent
e699df21c6
commit
839e8e8d0a
|
@ -1,135 +0,0 @@
|
||||||
import {makeAutoObservable, runInAction} from 'mobx'
|
|
||||||
import {AtUri} from '@atproto/api'
|
|
||||||
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
|
||||||
import {RootStoreModel} from '../root-store'
|
|
||||||
import {cleanError} from 'lib/strings/errors'
|
|
||||||
import {bundleAsync} from 'lib/async/bundle'
|
|
||||||
import * as apilib from 'lib/api/index'
|
|
||||||
import {logger} from '#/logger'
|
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
|
||||||
|
|
||||||
export type LikeItem = GetLikes.Like
|
|
||||||
|
|
||||||
export class LikesModel {
|
|
||||||
// state
|
|
||||||
isLoading = false
|
|
||||||
isRefreshing = false
|
|
||||||
hasLoaded = false
|
|
||||||
error = ''
|
|
||||||
resolvedUri = ''
|
|
||||||
params: GetLikes.QueryParams
|
|
||||||
hasMore = true
|
|
||||||
loadMoreCursor?: string
|
|
||||||
|
|
||||||
// data
|
|
||||||
uri: string = ''
|
|
||||||
likes: LikeItem[] = []
|
|
||||||
|
|
||||||
constructor(public rootStore: RootStoreModel, params: GetLikes.QueryParams) {
|
|
||||||
makeAutoObservable(
|
|
||||||
this,
|
|
||||||
{
|
|
||||||
rootStore: false,
|
|
||||||
params: false,
|
|
||||||
},
|
|
||||||
{autoBind: true},
|
|
||||||
)
|
|
||||||
this.params = params
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasContent() {
|
|
||||||
return this.uri !== ''
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasError() {
|
|
||||||
return this.error !== ''
|
|
||||||
}
|
|
||||||
|
|
||||||
get isEmpty() {
|
|
||||||
return this.hasLoaded && !this.hasContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// public api
|
|
||||||
// =
|
|
||||||
|
|
||||||
async refresh() {
|
|
||||||
return this.loadMore(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMore = bundleAsync(async (replace: boolean = false) => {
|
|
||||||
if (!replace && !this.hasMore) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this._xLoading(replace)
|
|
||||||
try {
|
|
||||||
if (!this.resolvedUri) {
|
|
||||||
await this._resolveUri()
|
|
||||||
}
|
|
||||||
const params = Object.assign({}, this.params, {
|
|
||||||
uri: this.resolvedUri,
|
|
||||||
limit: PAGE_SIZE,
|
|
||||||
cursor: replace ? undefined : this.loadMoreCursor,
|
|
||||||
})
|
|
||||||
const res = await this.rootStore.agent.getLikes(params)
|
|
||||||
if (replace) {
|
|
||||||
this._replaceAll(res)
|
|
||||||
} else {
|
|
||||||
this._appendAll(res)
|
|
||||||
}
|
|
||||||
this._xIdle()
|
|
||||||
} catch (e: any) {
|
|
||||||
this._xIdle(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// state transitions
|
|
||||||
// =
|
|
||||||
|
|
||||||
_xLoading(isRefreshing = false) {
|
|
||||||
this.isLoading = true
|
|
||||||
this.isRefreshing = isRefreshing
|
|
||||||
this.error = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
_xIdle(err?: any) {
|
|
||||||
this.isLoading = false
|
|
||||||
this.isRefreshing = false
|
|
||||||
this.hasLoaded = true
|
|
||||||
this.error = cleanError(err)
|
|
||||||
if (err) {
|
|
||||||
logger.error('Failed to fetch likes', {error: err})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper functions
|
|
||||||
// =
|
|
||||||
|
|
||||||
async _resolveUri() {
|
|
||||||
const urip = new AtUri(this.params.uri)
|
|
||||||
if (!urip.host.startsWith('did:')) {
|
|
||||||
try {
|
|
||||||
urip.host = await apilib.resolveName(this.rootStore, urip.host)
|
|
||||||
} catch (e: any) {
|
|
||||||
this.error = e.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runInAction(() => {
|
|
||||||
this.resolvedUri = urip.toString()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_replaceAll(res: GetLikes.Response) {
|
|
||||||
this.likes = []
|
|
||||||
this._appendAll(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
_appendAll(res: GetLikes.Response) {
|
|
||||||
this.loadMoreCursor = res.data.cursor
|
|
||||||
this.hasMore = !!this.loadMoreCursor
|
|
||||||
this.rootStore.me.follows.hydrateMany(
|
|
||||||
res.data.likes.map(like => like.actor),
|
|
||||||
)
|
|
||||||
this.likes = this.likes.concat(res.data.likes)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
import {makeAutoObservable, runInAction} from 'mobx'
|
|
||||||
import {AtUri} from '@atproto/api'
|
|
||||||
import {
|
|
||||||
AppBskyFeedGetRepostedBy as GetRepostedBy,
|
|
||||||
AppBskyActorDefs,
|
|
||||||
} from '@atproto/api'
|
|
||||||
import {RootStoreModel} from '../root-store'
|
|
||||||
import {bundleAsync} from 'lib/async/bundle'
|
|
||||||
import {cleanError} from 'lib/strings/errors'
|
|
||||||
import * as apilib from 'lib/api/index'
|
|
||||||
import {logger} from '#/logger'
|
|
||||||
|
|
||||||
const PAGE_SIZE = 30
|
|
||||||
|
|
||||||
export type RepostedByItem = AppBskyActorDefs.ProfileViewBasic
|
|
||||||
|
|
||||||
export class RepostedByModel {
|
|
||||||
// state
|
|
||||||
isLoading = false
|
|
||||||
isRefreshing = false
|
|
||||||
hasLoaded = false
|
|
||||||
error = ''
|
|
||||||
resolvedUri = ''
|
|
||||||
params: GetRepostedBy.QueryParams
|
|
||||||
hasMore = true
|
|
||||||
loadMoreCursor?: string
|
|
||||||
|
|
||||||
// data
|
|
||||||
uri: string = ''
|
|
||||||
repostedBy: RepostedByItem[] = []
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public rootStore: RootStoreModel,
|
|
||||||
params: GetRepostedBy.QueryParams,
|
|
||||||
) {
|
|
||||||
makeAutoObservable(
|
|
||||||
this,
|
|
||||||
{
|
|
||||||
rootStore: false,
|
|
||||||
params: false,
|
|
||||||
},
|
|
||||||
{autoBind: true},
|
|
||||||
)
|
|
||||||
this.params = params
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasContent() {
|
|
||||||
return this.uri !== ''
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasError() {
|
|
||||||
return this.error !== ''
|
|
||||||
}
|
|
||||||
|
|
||||||
get isEmpty() {
|
|
||||||
return this.hasLoaded && !this.hasContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// public api
|
|
||||||
// =
|
|
||||||
|
|
||||||
async refresh() {
|
|
||||||
return this.loadMore(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMore = bundleAsync(async (replace: boolean = false) => {
|
|
||||||
this._xLoading(replace)
|
|
||||||
try {
|
|
||||||
if (!this.resolvedUri) {
|
|
||||||
await this._resolveUri()
|
|
||||||
}
|
|
||||||
const params = Object.assign({}, this.params, {
|
|
||||||
uri: this.resolvedUri,
|
|
||||||
limit: PAGE_SIZE,
|
|
||||||
cursor: replace ? undefined : this.loadMoreCursor,
|
|
||||||
})
|
|
||||||
const res = await this.rootStore.agent.getRepostedBy(params)
|
|
||||||
if (replace) {
|
|
||||||
this._replaceAll(res)
|
|
||||||
} else {
|
|
||||||
this._appendAll(res)
|
|
||||||
}
|
|
||||||
this._xIdle()
|
|
||||||
} catch (e: any) {
|
|
||||||
this._xIdle(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// state transitions
|
|
||||||
// =
|
|
||||||
|
|
||||||
_xLoading(isRefreshing = false) {
|
|
||||||
this.isLoading = true
|
|
||||||
this.isRefreshing = isRefreshing
|
|
||||||
this.error = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
_xIdle(err?: any) {
|
|
||||||
this.isLoading = false
|
|
||||||
this.isRefreshing = false
|
|
||||||
this.hasLoaded = true
|
|
||||||
this.error = cleanError(err)
|
|
||||||
if (err) {
|
|
||||||
logger.error('Failed to fetch reposted by view', {error: err})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper functions
|
|
||||||
// =
|
|
||||||
|
|
||||||
async _resolveUri() {
|
|
||||||
const urip = new AtUri(this.params.uri)
|
|
||||||
if (!urip.host.startsWith('did:')) {
|
|
||||||
try {
|
|
||||||
urip.host = await apilib.resolveName(this.rootStore, urip.host)
|
|
||||||
} catch (e: any) {
|
|
||||||
this.error = e.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runInAction(() => {
|
|
||||||
this.resolvedUri = urip.toString()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_replaceAll(res: GetRepostedBy.Response) {
|
|
||||||
this.repostedBy = []
|
|
||||||
this._appendAll(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
_appendAll(res: GetRepostedBy.Response) {
|
|
||||||
this.loadMoreCursor = res.data.cursor
|
|
||||||
this.hasMore = !!this.loadMoreCursor
|
|
||||||
this.repostedBy = this.repostedBy.concat(res.data.repostedBy)
|
|
||||||
this.rootStore.me.follows.hydrateMany(res.data.repostedBy)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import {AppBskyFeedGetLikes} from '@atproto/api'
|
||||||
|
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||||
|
import {useSession} from '../session'
|
||||||
|
|
||||||
|
const PAGE_SIZE = 30
|
||||||
|
type RQPageParam = string | undefined
|
||||||
|
|
||||||
|
export const RQKEY = (resolvedUri: string) => ['post-liked-by', resolvedUri]
|
||||||
|
|
||||||
|
export function usePostLikedByQuery(resolvedUri: string | undefined) {
|
||||||
|
const {agent} = useSession()
|
||||||
|
return useInfiniteQuery<
|
||||||
|
AppBskyFeedGetLikes.OutputSchema,
|
||||||
|
Error,
|
||||||
|
InfiniteData<AppBskyFeedGetLikes.OutputSchema>,
|
||||||
|
QueryKey,
|
||||||
|
RQPageParam
|
||||||
|
>({
|
||||||
|
queryKey: RQKEY(resolvedUri || ''),
|
||||||
|
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||||
|
const res = await agent.getLikes({
|
||||||
|
uri: resolvedUri || '',
|
||||||
|
limit: PAGE_SIZE,
|
||||||
|
cursor: pageParam,
|
||||||
|
})
|
||||||
|
return res.data
|
||||||
|
},
|
||||||
|
initialPageParam: undefined,
|
||||||
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
|
enabled: !!resolvedUri,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import {AppBskyFeedGetRepostedBy} from '@atproto/api'
|
||||||
|
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||||
|
import {useSession} from '../session'
|
||||||
|
|
||||||
|
const PAGE_SIZE = 30
|
||||||
|
type RQPageParam = string | undefined
|
||||||
|
|
||||||
|
export const RQKEY = (resolvedUri: string) => ['post-reposted-by', resolvedUri]
|
||||||
|
|
||||||
|
export function usePostRepostedByQuery(resolvedUri: string | undefined) {
|
||||||
|
const {agent} = useSession()
|
||||||
|
return useInfiniteQuery<
|
||||||
|
AppBskyFeedGetRepostedBy.OutputSchema,
|
||||||
|
Error,
|
||||||
|
InfiniteData<AppBskyFeedGetRepostedBy.OutputSchema>,
|
||||||
|
QueryKey,
|
||||||
|
RQPageParam
|
||||||
|
>({
|
||||||
|
queryKey: RQKEY(resolvedUri || ''),
|
||||||
|
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||||
|
const res = await agent.getRepostedBy({
|
||||||
|
uri: resolvedUri || '',
|
||||||
|
limit: PAGE_SIZE,
|
||||||
|
cursor: pageParam,
|
||||||
|
})
|
||||||
|
return res.data
|
||||||
|
},
|
||||||
|
initialPageParam: undefined,
|
||||||
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
|
enabled: !!resolvedUri,
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,39 +1,74 @@
|
||||||
import React, {useEffect} from 'react'
|
import React, {useCallback, useMemo, useState} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
|
||||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||||
|
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
||||||
import {CenteredView, FlatList} from '../util/Views'
|
import {CenteredView, FlatList} from '../util/Views'
|
||||||
import {LikesModel, LikeItem} from 'state/models/lists/likes'
|
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||||
|
import {usePostLikedByQuery} from '#/state/queries/post-liked-by'
|
||||||
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
|
||||||
export const PostLikedBy = observer(function PostLikedByImpl({
|
export function PostLikedBy({uri}: {uri: string}) {
|
||||||
uri,
|
|
||||||
}: {
|
|
||||||
uri: string
|
|
||||||
}) {
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
const view = React.useMemo(() => new LikesModel(store, {uri}), [store, uri])
|
const {
|
||||||
|
data: resolvedUri,
|
||||||
useEffect(() => {
|
error: resolveError,
|
||||||
view
|
isFetching: isFetchingResolvedUri,
|
||||||
.loadMore()
|
} = useResolveUriQuery(uri)
|
||||||
.catch(err => logger.error('Failed to fetch likes', {error: err}))
|
const {
|
||||||
}, [view])
|
data,
|
||||||
|
dataUpdatedAt,
|
||||||
const onRefresh = () => {
|
isFetching,
|
||||||
view.refresh()
|
isFetched,
|
||||||
}
|
isFetchingNextPage,
|
||||||
const onEndReached = () => {
|
hasNextPage,
|
||||||
view
|
fetchNextPage,
|
||||||
.loadMore()
|
isError,
|
||||||
.catch(err => logger.error('Failed to load more likes', {error: err}))
|
error,
|
||||||
|
refetch,
|
||||||
|
} = usePostLikedByQuery(resolvedUri?.uri)
|
||||||
|
const likes = useMemo(() => {
|
||||||
|
if (data?.pages) {
|
||||||
|
return data.pages.flatMap(page => page.likes)
|
||||||
}
|
}
|
||||||
|
}, [data])
|
||||||
|
|
||||||
if (!view.hasLoaded) {
|
const onRefresh = useCallback(async () => {
|
||||||
|
setIsPTRing(true)
|
||||||
|
try {
|
||||||
|
await refetch()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to refresh likes', {error: err})
|
||||||
|
}
|
||||||
|
setIsPTRing(false)
|
||||||
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
|
const onEndReached = useCallback(async () => {
|
||||||
|
if (isFetching || !hasNextPage || isError) return
|
||||||
|
try {
|
||||||
|
await fetchNextPage()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to load more likes', {error: err})
|
||||||
|
}
|
||||||
|
}, [isFetching, hasNextPage, isError, fetchNextPage])
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({item}: {item: GetLikes.Like}) => {
|
||||||
|
return (
|
||||||
|
<ProfileCardWithFollowBtn
|
||||||
|
key={item.actor.did}
|
||||||
|
profile={item.actor}
|
||||||
|
dataUpdatedAt={dataUpdatedAt}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[dataUpdatedAt],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isFetchingResolvedUri || !isFetched) {
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
|
@ -43,26 +78,26 @@ export const PostLikedBy = observer(function PostLikedByImpl({
|
||||||
|
|
||||||
// error
|
// error
|
||||||
// =
|
// =
|
||||||
if (view.hasError) {
|
if (resolveError || isError) {
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
<ErrorMessage
|
||||||
|
message={cleanError(resolveError || error)}
|
||||||
|
onPressTryAgain={onRefresh}
|
||||||
|
/>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loaded
|
// loaded
|
||||||
// =
|
// =
|
||||||
const renderItem = ({item}: {item: LikeItem}) => (
|
|
||||||
<ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={view.likes}
|
data={likes}
|
||||||
keyExtractor={item => item.actor.did}
|
keyExtractor={item => item.actor.did}
|
||||||
refreshControl={
|
refreshControl={
|
||||||
<RefreshControl
|
<RefreshControl
|
||||||
refreshing={view.isRefreshing}
|
refreshing={isPTRing}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
tintColor={pal.colors.text}
|
tintColor={pal.colors.text}
|
||||||
titleColor={pal.colors.text}
|
titleColor={pal.colors.text}
|
||||||
|
@ -75,15 +110,14 @@ export const PostLikedBy = observer(function PostLikedByImpl({
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
// eslint-disable-next-line react/no-unstable-nested-components
|
||||||
ListFooterComponent={() => (
|
ListFooterComponent={() => (
|
||||||
<View style={styles.footer}>
|
<View style={styles.footer}>
|
||||||
{view.isLoading && <ActivityIndicator />}
|
{(isFetching || isFetchingNextPage) && <ActivityIndicator />}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
extraData={view.isLoading}
|
|
||||||
// @ts-ignore our .web version only -prf
|
// @ts-ignore our .web version only -prf
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
footer: {
|
footer: {
|
||||||
|
|
|
@ -1,42 +1,74 @@
|
||||||
import React, {useEffect} from 'react'
|
import React, {useMemo, useCallback, useState} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
|
||||||
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
|
||||||
|
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||||
import {CenteredView, FlatList} from '../util/Views'
|
import {CenteredView, FlatList} from '../util/Views'
|
||||||
import {RepostedByModel, RepostedByItem} from 'state/models/lists/reposted-by'
|
|
||||||
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||||
|
import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by'
|
||||||
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
|
||||||
export const PostRepostedBy = observer(function PostRepostedByImpl({
|
export function PostRepostedBy({uri}: {uri: string}) {
|
||||||
uri,
|
|
||||||
}: {
|
|
||||||
uri: string
|
|
||||||
}) {
|
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
const view = React.useMemo(
|
const {
|
||||||
() => new RepostedByModel(store, {uri}),
|
data: resolvedUri,
|
||||||
[store, uri],
|
error: resolveError,
|
||||||
|
isFetching: isFetchingResolvedUri,
|
||||||
|
} = useResolveUriQuery(uri)
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
dataUpdatedAt,
|
||||||
|
isFetching,
|
||||||
|
isFetched,
|
||||||
|
isFetchingNextPage,
|
||||||
|
hasNextPage,
|
||||||
|
fetchNextPage,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
refetch,
|
||||||
|
} = usePostRepostedByQuery(resolvedUri?.uri)
|
||||||
|
const repostedBy = useMemo(() => {
|
||||||
|
if (data?.pages) {
|
||||||
|
return data.pages.flatMap(page => page.repostedBy)
|
||||||
|
}
|
||||||
|
}, [data])
|
||||||
|
|
||||||
|
const onRefresh = useCallback(async () => {
|
||||||
|
setIsPTRing(true)
|
||||||
|
try {
|
||||||
|
await refetch()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to refresh reposts', {error: err})
|
||||||
|
}
|
||||||
|
setIsPTRing(false)
|
||||||
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
|
const onEndReached = useCallback(async () => {
|
||||||
|
if (isFetching || !hasNextPage || isError) return
|
||||||
|
try {
|
||||||
|
await fetchNextPage()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to load more reposts', {error: err})
|
||||||
|
}
|
||||||
|
}, [isFetching, hasNextPage, isError, fetchNextPage])
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({item}: {item: ActorDefs.ProfileViewBasic}) => {
|
||||||
|
return (
|
||||||
|
<ProfileCardWithFollowBtn
|
||||||
|
key={item.did}
|
||||||
|
profile={item}
|
||||||
|
dataUpdatedAt={dataUpdatedAt}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[dataUpdatedAt],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
if (isFetchingResolvedUri || !isFetched) {
|
||||||
view
|
|
||||||
.loadMore()
|
|
||||||
.catch(err => logger.error('Failed to fetch reposts', {error: err}))
|
|
||||||
}, [view])
|
|
||||||
|
|
||||||
const onRefresh = () => {
|
|
||||||
view.refresh()
|
|
||||||
}
|
|
||||||
const onEndReached = () => {
|
|
||||||
view
|
|
||||||
.loadMore()
|
|
||||||
.catch(err => logger.error('Failed to load more reposts', {error: err}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!view.hasLoaded) {
|
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
|
@ -46,26 +78,26 @@ export const PostRepostedBy = observer(function PostRepostedByImpl({
|
||||||
|
|
||||||
// error
|
// error
|
||||||
// =
|
// =
|
||||||
if (view.hasError) {
|
if (resolveError || isError) {
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<ErrorMessage message={view.error} onPressTryAgain={onRefresh} />
|
<ErrorMessage
|
||||||
|
message={cleanError(resolveError || error)}
|
||||||
|
onPressTryAgain={onRefresh}
|
||||||
|
/>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loaded
|
// loaded
|
||||||
// =
|
// =
|
||||||
const renderItem = ({item}: {item: RepostedByItem}) => (
|
|
||||||
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={view.repostedBy}
|
data={repostedBy}
|
||||||
keyExtractor={item => item.did}
|
keyExtractor={item => item.did}
|
||||||
refreshControl={
|
refreshControl={
|
||||||
<RefreshControl
|
<RefreshControl
|
||||||
refreshing={view.isRefreshing}
|
refreshing={isPTRing}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
tintColor={pal.colors.text}
|
tintColor={pal.colors.text}
|
||||||
titleColor={pal.colors.text}
|
titleColor={pal.colors.text}
|
||||||
|
@ -78,15 +110,14 @@ export const PostRepostedBy = observer(function PostRepostedByImpl({
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
// eslint-disable-next-line react/no-unstable-nested-components
|
||||||
ListFooterComponent={() => (
|
ListFooterComponent={() => (
|
||||||
<View style={styles.footer}>
|
<View style={styles.footer}>
|
||||||
{view.isLoading && <ActivityIndicator />}
|
{(isFetching || isFetchingNextPage) && <ActivityIndicator />}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
extraData={view.isLoading}
|
|
||||||
// @ts-ignore our .web version only -prf
|
// @ts-ignore our .web version only -prf
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
footer: {
|
footer: {
|
||||||
|
|
Loading…
Reference in New Issue