about summary refs log tree commit diff
path: root/src/view/com/util/post-ctrls
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util/post-ctrls')
-rw-r--r--src/view/com/util/post-ctrls/PostCtrls.tsx204
-rw-r--r--src/view/com/util/post-ctrls/RepostButton.tsx14
-rw-r--r--src/view/com/util/post-ctrls/RepostButton.web.tsx67
3 files changed, 158 insertions, 127 deletions
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index 5769a478b..e548c45f7 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -6,168 +6,174 @@ import {
   View,
   ViewStyle,
 } from 'react-native'
+import {AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
 import {Text} from '../text/Text'
 import {PostDropdownBtn} from '../forms/PostDropdownBtn'
 import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
 import {s, colors} from 'lib/styles'
 import {pluralize} from 'lib/strings/helpers'
 import {useTheme} from 'lib/ThemeContext'
-import {useStores} from 'state/index'
 import {RepostButton} from './RepostButton'
 import {Haptics} from 'lib/haptics'
 import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
+import {useModalControls} from '#/state/modals'
+import {
+  usePostLikeMutation,
+  usePostUnlikeMutation,
+  usePostRepostMutation,
+  usePostUnrepostMutation,
+} from '#/state/queries/post'
+import {useComposerControls} from '#/state/shell/composer'
+import {Shadow} from '#/state/cache/types'
+import {useRequireAuth} from '#/state/session'
 
-interface PostCtrlsOpts {
-  itemUri: string
-  itemCid: string
-  itemHref: string
-  itemTitle: string
-  isAuthor: boolean
-  author: {
-    did: string
-    handle: string
-    displayName?: string | undefined
-    avatar?: string | undefined
-  }
-  text: string
-  indexedAt: string
+export function PostCtrls({
+  big,
+  post,
+  record,
+  style,
+  onPressReply,
+}: {
   big?: boolean
+  post: Shadow<AppBskyFeedDefs.PostView>
+  record: AppBskyFeedPost.Record
   style?: StyleProp<ViewStyle>
-  replyCount?: number
-  repostCount?: number
-  likeCount?: number
-  isReposted: boolean
-  isLiked: boolean
-  isThreadMuted: boolean
   onPressReply: () => void
-  onPressToggleRepost: () => Promise<void>
-  onPressToggleLike: () => Promise<void>
-  onCopyPostText: () => void
-  onOpenTranslate: () => void
-  onToggleThreadMute: () => void
-  onDeletePost: () => void
-}
-
-export function PostCtrls(opts: PostCtrlsOpts) {
-  const store = useStores()
+}) {
   const theme = useTheme()
+  const {openComposer} = useComposerControls()
+  const {closeModal} = useModalControls()
+  const postLikeMutation = usePostLikeMutation()
+  const postUnlikeMutation = usePostUnlikeMutation()
+  const postRepostMutation = usePostRepostMutation()
+  const postUnrepostMutation = usePostUnrepostMutation()
+  const requireAuth = useRequireAuth()
+
   const defaultCtrlColor = React.useMemo(
     () => ({
       color: theme.palette.default.postCtrl,
     }),
     [theme],
   ) as StyleProp<ViewStyle>
+
+  const onPressToggleLike = React.useCallback(async () => {
+    if (!post.viewer?.like) {
+      Haptics.default()
+      postLikeMutation.mutate({
+        uri: post.uri,
+        cid: post.cid,
+        likeCount: post.likeCount || 0,
+      })
+    } else {
+      postUnlikeMutation.mutate({
+        postUri: post.uri,
+        likeUri: post.viewer.like,
+        likeCount: post.likeCount || 0,
+      })
+    }
+  }, [post, postLikeMutation, postUnlikeMutation])
+
   const onRepost = useCallback(() => {
-    store.shell.closeModal()
-    if (!opts.isReposted) {
+    closeModal()
+    if (!post.viewer?.repost) {
       Haptics.default()
-      opts.onPressToggleRepost().catch(_e => undefined)
+      postRepostMutation.mutate({
+        uri: post.uri,
+        cid: post.cid,
+        repostCount: post.repostCount || 0,
+      })
     } else {
-      opts.onPressToggleRepost().catch(_e => undefined)
+      postUnrepostMutation.mutate({
+        postUri: post.uri,
+        repostUri: post.viewer.repost,
+        repostCount: post.repostCount || 0,
+      })
     }
-  }, [opts, store.shell])
+  }, [post, closeModal, postRepostMutation, postUnrepostMutation])
 
   const onQuote = useCallback(() => {
-    store.shell.closeModal()
-    store.shell.openComposer({
+    closeModal()
+    openComposer({
       quote: {
-        uri: opts.itemUri,
-        cid: opts.itemCid,
-        text: opts.text,
-        author: opts.author,
-        indexedAt: opts.indexedAt,
+        uri: post.uri,
+        cid: post.cid,
+        text: record.text,
+        author: post.author,
+        indexedAt: post.indexedAt,
       },
     })
     Haptics.default()
-  }, [
-    opts.author,
-    opts.indexedAt,
-    opts.itemCid,
-    opts.itemUri,
-    opts.text,
-    store.shell,
-  ])
-
-  const onPressToggleLikeWrapper = async () => {
-    if (!opts.isLiked) {
-      Haptics.default()
-      await opts.onPressToggleLike().catch(_e => undefined)
-    } else {
-      await opts.onPressToggleLike().catch(_e => undefined)
-    }
-  }
-
+  }, [post, record, openComposer, closeModal])
   return (
-    <View style={[styles.ctrls, opts.style]}>
+    <View style={[styles.ctrls, style]}>
       <TouchableOpacity
         testID="replyBtn"
-        style={[styles.ctrl, !opts.big && styles.ctrlPad, {paddingLeft: 0}]}
-        onPress={opts.onPressReply}
+        style={[styles.ctrl, !big && styles.ctrlPad, {paddingLeft: 0}]}
+        onPress={() => {
+          requireAuth(() => onPressReply())
+        }}
         accessibilityRole="button"
-        accessibilityLabel={`Reply (${opts.replyCount} ${
-          opts.replyCount === 1 ? 'reply' : 'replies'
+        accessibilityLabel={`Reply (${post.replyCount} ${
+          post.replyCount === 1 ? 'reply' : 'replies'
         })`}
         accessibilityHint=""
-        hitSlop={opts.big ? HITSLOP_20 : HITSLOP_10}>
+        hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
         <CommentBottomArrow
-          style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]}
+          style={[defaultCtrlColor, big ? s.mt2 : styles.mt1]}
           strokeWidth={3}
-          size={opts.big ? 20 : 15}
+          size={big ? 20 : 15}
         />
-        {typeof opts.replyCount !== 'undefined' ? (
+        {typeof post.replyCount !== 'undefined' ? (
           <Text style={[defaultCtrlColor, s.ml5, s.f15]}>
-            {opts.replyCount}
+            {post.replyCount}
           </Text>
         ) : undefined}
       </TouchableOpacity>
-      <RepostButton {...opts} onRepost={onRepost} onQuote={onQuote} />
+      <RepostButton
+        big={big}
+        isReposted={!!post.viewer?.repost}
+        repostCount={post.repostCount}
+        onRepost={onRepost}
+        onQuote={onQuote}
+      />
       <TouchableOpacity
         testID="likeBtn"
-        style={[styles.ctrl, !opts.big && styles.ctrlPad]}
-        onPress={onPressToggleLikeWrapper}
+        style={[styles.ctrl, !big && styles.ctrlPad]}
+        onPress={() => {
+          requireAuth(() => onPressToggleLike())
+        }}
         accessibilityRole="button"
-        accessibilityLabel={`${opts.isLiked ? 'Unlike' : 'Like'} (${
-          opts.likeCount
-        } ${pluralize(opts.likeCount || 0, 'like')})`}
+        accessibilityLabel={`${post.viewer?.like ? 'Unlike' : 'Like'} (${
+          post.likeCount
+        } ${pluralize(post.likeCount || 0, 'like')})`}
         accessibilityHint=""
-        hitSlop={opts.big ? HITSLOP_20 : HITSLOP_10}>
-        {opts.isLiked ? (
-          <HeartIconSolid
-            style={styles.ctrlIconLiked}
-            size={opts.big ? 22 : 16}
-          />
+        hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
+        {post.viewer?.like ? (
+          <HeartIconSolid style={styles.ctrlIconLiked} size={big ? 22 : 16} />
         ) : (
           <HeartIcon
-            style={[defaultCtrlColor, opts.big ? styles.mt1 : undefined]}
+            style={[defaultCtrlColor, big ? styles.mt1 : undefined]}
             strokeWidth={3}
-            size={opts.big ? 20 : 16}
+            size={big ? 20 : 16}
           />
         )}
-        {typeof opts.likeCount !== 'undefined' ? (
+        {typeof post.likeCount !== 'undefined' ? (
           <Text
             testID="likeCount"
             style={
-              opts.isLiked
+              post.viewer?.like
                 ? [s.bold, s.red3, s.f15, s.ml5]
                 : [defaultCtrlColor, s.f15, s.ml5]
             }>
-            {opts.likeCount}
+            {post.likeCount}
           </Text>
         ) : undefined}
       </TouchableOpacity>
-      {opts.big ? undefined : (
+      {big ? undefined : (
         <PostDropdownBtn
           testID="postDropdownBtn"
-          itemUri={opts.itemUri}
-          itemCid={opts.itemCid}
-          itemHref={opts.itemHref}
-          itemTitle={opts.itemTitle}
-          isAuthor={opts.isAuthor}
-          isThreadMuted={opts.isThreadMuted}
-          onCopyPostText={opts.onCopyPostText}
-          onOpenTranslate={opts.onOpenTranslate}
-          onToggleThreadMute={opts.onToggleThreadMute}
-          onDeletePost={opts.onDeletePost}
+          post={post}
+          record={record}
           style={styles.ctrlPad}
         />
       )}
diff --git a/src/view/com/util/post-ctrls/RepostButton.tsx b/src/view/com/util/post-ctrls/RepostButton.tsx
index 9c4ed8e5d..1d34a88ab 100644
--- a/src/view/com/util/post-ctrls/RepostButton.tsx
+++ b/src/view/com/util/post-ctrls/RepostButton.tsx
@@ -5,8 +5,9 @@ import {s, colors} from 'lib/styles'
 import {useTheme} from 'lib/ThemeContext'
 import {Text} from '../text/Text'
 import {pluralize} from 'lib/strings/helpers'
-import {useStores} from 'state/index'
 import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
+import {useModalControls} from '#/state/modals'
+import {useRequireAuth} from '#/state/session'
 
 interface Props {
   isReposted: boolean
@@ -23,8 +24,9 @@ export const RepostButton = ({
   onRepost,
   onQuote,
 }: Props) => {
-  const store = useStores()
   const theme = useTheme()
+  const {openModal} = useModalControls()
+  const requireAuth = useRequireAuth()
 
   const defaultControlColor = React.useMemo(
     () => ({
@@ -34,18 +36,20 @@ export const RepostButton = ({
   )
 
   const onPressToggleRepostWrapper = useCallback(() => {
-    store.shell.openModal({
+    openModal({
       name: 'repost',
       onRepost: onRepost,
       onQuote: onQuote,
       isReposted,
     })
-  }, [onRepost, onQuote, isReposted, store.shell])
+  }, [onRepost, onQuote, isReposted, openModal])
 
   return (
     <TouchableOpacity
       testID="repostBtn"
-      onPress={onPressToggleRepostWrapper}
+      onPress={() => {
+        requireAuth(() => onPressToggleRepostWrapper())
+      }}
       style={[styles.control, !big && styles.controlPad]}
       accessibilityRole="button"
       accessibilityLabel={`${
diff --git a/src/view/com/util/post-ctrls/RepostButton.web.tsx b/src/view/com/util/post-ctrls/RepostButton.web.tsx
index 57f544d41..329382132 100644
--- a/src/view/com/util/post-ctrls/RepostButton.web.tsx
+++ b/src/view/com/util/post-ctrls/RepostButton.web.tsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
+import {StyleProp, StyleSheet, View, ViewStyle, Pressable} from 'react-native'
 import {RepostIcon} from 'lib/icons'
 import {colors} from 'lib/styles'
 import {useTheme} from 'lib/ThemeContext'
@@ -10,6 +10,10 @@ import {
   DropdownItem as NativeDropdownItem,
 } from '../forms/NativeDropdown'
 import {EventStopper} from '../EventStopper'
+import {useLingui} from '@lingui/react'
+import {msg} from '@lingui/macro'
+import {useRequireAuth} from '#/state/session'
+import {useSession} from '#/state/session'
 
 interface Props {
   isReposted: boolean
@@ -28,6 +32,9 @@ export const RepostButton = ({
   onQuote,
 }: Props) => {
   const theme = useTheme()
+  const {_} = useLingui()
+  const {hasSession} = useSession()
+  const requireAuth = useRequireAuth()
 
   const defaultControlColor = React.useMemo(
     () => ({
@@ -38,7 +45,7 @@ export const RepostButton = ({
 
   const dropdownItems: NativeDropdownItem[] = [
     {
-      label: isReposted ? 'Undo repost' : 'Repost',
+      label: isReposted ? _(msg`Undo repost`) : _(msg`Repost`),
       testID: 'repostDropdownRepostBtn',
       icon: {
         ios: {name: 'repeat'},
@@ -48,7 +55,7 @@ export const RepostButton = ({
       onPress: onRepost,
     },
     {
-      label: 'Quote post',
+      label: _(msg`Quote post`),
       testID: 'repostDropdownQuoteBtn',
       icon: {
         ios: {name: 'quote.bubble'},
@@ -59,32 +66,46 @@ export const RepostButton = ({
     },
   ]
 
-  return (
+  const inner = (
+    <View
+      style={[
+        styles.control,
+        !big && styles.controlPad,
+        (isReposted
+          ? styles.reposted
+          : defaultControlColor) as StyleProp<ViewStyle>,
+      ]}>
+      <RepostIcon strokeWidth={2.2} size={big ? 24 : 20} />
+      {typeof repostCount !== 'undefined' ? (
+        <Text
+          testID="repostCount"
+          type={isReposted ? 'md-bold' : 'md'}
+          style={styles.repostCount}>
+          {repostCount ?? 0}
+        </Text>
+      ) : undefined}
+    </View>
+  )
+
+  return hasSession ? (
     <EventStopper>
       <NativeDropdown
         items={dropdownItems}
-        accessibilityLabel="Repost or quote post"
+        accessibilityLabel={_(msg`Repost or quote post`)}
         accessibilityHint="">
-        <View
-          style={[
-            styles.control,
-            !big && styles.controlPad,
-            (isReposted
-              ? styles.reposted
-              : defaultControlColor) as StyleProp<ViewStyle>,
-          ]}>
-          <RepostIcon strokeWidth={2.2} size={big ? 24 : 20} />
-          {typeof repostCount !== 'undefined' ? (
-            <Text
-              testID="repostCount"
-              type={isReposted ? 'md-bold' : 'md'}
-              style={styles.repostCount}>
-              {repostCount ?? 0}
-            </Text>
-          ) : undefined}
-        </View>
+        {inner}
       </NativeDropdown>
     </EventStopper>
+  ) : (
+    <Pressable
+      accessibilityRole="button"
+      onPress={() => {
+        requireAuth(() => {})
+      }}
+      accessibilityLabel={_(msg`Repost or quote post`)}
+      accessibilityHint="">
+      {inner}
+    </Pressable>
   )
 }