about summary refs log tree commit diff
path: root/src/view/com/post-thread/PostQuotes.tsx
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-08-21 21:26:25 +0100
committerGitHub <noreply@github.com>2024-08-21 15:26:25 -0500
commit56ab5e177fa2b24d0e5d9d969aa37532b96128da (patch)
tree2fa3db0ef9e46474aac00d5a593c5e5d592da9e3 /src/view/com/post-thread/PostQuotes.tsx
parentddb0b80017c2b5bc158b8ff9da222abd5a8bf025 (diff)
downloadvoidsky-56ab5e177fa2b24d0e5d9d969aa37532b96128da.tar.zst
Show quote posts (#4865)
* show quote posts

* fix filter

* fix keyExtractor

* move likedby and repostedby to new file structure

* use modern list component

* remove relative imports

* update quotes count after quoting

* call `onPost` after updating quote count

* Revert "update quotes count after quoting"

This reverts commit 1f1887730a210c57c1e5a0eb0f47c42c42cf1b4b.

* implement

* update like count in quotes list

* only add `onPostReply` where needed

* Filter quotes with detached embeds

* Bump SDK

* Don't show error for no results

---------

Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>
Co-authored-by: Hailey <me@haileyok.com>
Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src/view/com/post-thread/PostQuotes.tsx')
-rw-r--r--src/view/com/post-thread/PostQuotes.tsx141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/view/com/post-thread/PostQuotes.tsx b/src/view/com/post-thread/PostQuotes.tsx
new file mode 100644
index 000000000..d573d27a1
--- /dev/null
+++ b/src/view/com/post-thread/PostQuotes.tsx
@@ -0,0 +1,141 @@
+import React, {useCallback, useState} from 'react'
+import {
+  AppBskyFeedDefs,
+  AppBskyFeedPost,
+  ModerationDecision,
+} from '@atproto/api'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
+import {cleanError} from '#/lib/strings/errors'
+import {logger} from '#/logger'
+import {isWeb} from '#/platform/detection'
+import {useModerationOpts} from '#/state/preferences/moderation-opts'
+import {usePostQuotesQuery} from '#/state/queries/post-quotes'
+import {useResolveUriQuery} from '#/state/queries/resolve-uri'
+import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
+import {Post} from 'view/com/post/Post'
+import {
+  ListFooter,
+  ListHeaderDesktop,
+  ListMaybePlaceholder,
+} from '#/components/Lists'
+import {List} from '../util/List'
+
+function renderItem({
+  item,
+  index,
+}: {
+  item: {
+    post: AppBskyFeedDefs.PostView
+    moderation: ModerationDecision
+    record: AppBskyFeedPost.Record
+  }
+  index: number
+}) {
+  return <Post post={item.post} hideTopBorder={index === 0 && !isWeb} />
+}
+
+function keyExtractor(item: {
+  post: AppBskyFeedDefs.PostView
+  moderation: ModerationDecision
+  record: AppBskyFeedPost.Record
+}) {
+  return item.post.uri
+}
+
+export function PostQuotes({uri}: {uri: string}) {
+  const {_} = useLingui()
+  const initialNumToRender = useInitialNumToRender()
+
+  const [isPTRing, setIsPTRing] = useState(false)
+
+  const {
+    data: resolvedUri,
+    error: resolveError,
+    isLoading: isLoadingUri,
+  } = useResolveUriQuery(uri)
+  const {
+    data,
+    isLoading: isLoadingQuotes,
+    isFetchingNextPage,
+    hasNextPage,
+    fetchNextPage,
+    error,
+    refetch,
+  } = usePostQuotesQuery(resolvedUri?.uri)
+
+  const moderationOpts = useModerationOpts()
+
+  const isError = Boolean(resolveError || error)
+
+  const quotes =
+    data?.pages
+      .flatMap(page =>
+        page.posts.map(post => {
+          if (!AppBskyFeedPost.isRecord(post.record) || !moderationOpts) {
+            return null
+          }
+          const moderation = moderatePost(post, moderationOpts)
+          return {post, record: post.record, moderation}
+        }),
+      )
+      .filter(item => item !== null) ?? []
+
+  const onRefresh = useCallback(async () => {
+    setIsPTRing(true)
+    try {
+      await refetch()
+    } catch (err) {
+      logger.error('Failed to refresh quotes', {message: err})
+    }
+    setIsPTRing(false)
+  }, [refetch, setIsPTRing])
+
+  const onEndReached = useCallback(async () => {
+    if (isFetchingNextPage || !hasNextPage || isError) return
+    try {
+      await fetchNextPage()
+    } catch (err) {
+      logger.error('Failed to load more quotes', {message: err})
+    }
+  }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
+
+  if (isLoadingUri || isLoadingQuotes || isError) {
+    return (
+      <ListMaybePlaceholder
+        isLoading={isLoadingUri || isLoadingQuotes}
+        isError={isError}
+      />
+    )
+  }
+
+  // loaded
+  // =
+  return (
+    <List
+      data={quotes}
+      renderItem={renderItem}
+      keyExtractor={keyExtractor}
+      refreshing={isPTRing}
+      onRefresh={onRefresh}
+      onEndReached={onEndReached}
+      onEndReachedThreshold={4}
+      ListHeaderComponent={<ListHeaderDesktop title={_(msg`Quotes`)} />}
+      ListFooterComponent={
+        <ListFooter
+          isFetchingNextPage={isFetchingNextPage}
+          error={cleanError(error)}
+          onRetry={fetchNextPage}
+          showEndMessage
+          endMessageText={_(msg`That's all, folks!`)}
+        />
+      }
+      // @ts-ignore our .web version only -prf
+      desktopFixedHeight
+      initialNumToRender={initialNumToRender}
+      windowSize={11}
+    />
+  )
+}