diff options
Diffstat (limited to 'src/view/com/util/post-ctrls')
-rw-r--r-- | src/view/com/util/post-ctrls/PostCtrls.tsx | 204 | ||||
-rw-r--r-- | src/view/com/util/post-ctrls/RepostButton.tsx | 14 | ||||
-rw-r--r-- | src/view/com/util/post-ctrls/RepostButton.web.tsx | 67 |
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> ) } |