From 628d8773255d403347b0cbb98afa6681768d428e Mon Sep 17 00:00:00 2001 From: Ollie H Date: Mon, 15 May 2023 13:18:39 -0700 Subject: Use dropdown for web reposting and quote posting (#607) * Use dropdown for web reposting and quote posting * Remove collateral damage * Tune the repost dropdown positioning * Move postctrls into their own folder * Factor out repost button into native/web build --------- Co-authored-by: Paul Frazee --- src/view/com/post-thread/PostThreadItem.tsx | 2 +- src/view/com/post/Post.tsx | 2 +- src/view/com/posts/FeedItem.tsx | 2 +- src/view/com/util/PostCtrls.tsx | 321 ---------------------- src/view/com/util/post-ctrls/PostCtrls.tsx | 283 +++++++++++++++++++ src/view/com/util/post-ctrls/RepostButton.tsx | 95 +++++++ src/view/com/util/post-ctrls/RepostButton.web.tsx | 86 ++++++ 7 files changed, 467 insertions(+), 324 deletions(-) delete mode 100644 src/view/com/util/PostCtrls.tsx create mode 100644 src/view/com/util/post-ctrls/PostCtrls.tsx create mode 100644 src/view/com/util/post-ctrls/RepostButton.tsx create mode 100644 src/view/com/util/post-ctrls/RepostButton.web.tsx (limited to 'src') diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 563a3ead6..084e30a25 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -21,7 +21,7 @@ import {pluralize} from 'lib/strings/helpers' import {useStores} from 'state/index' import {PostMeta} from '../util/PostMeta' import {PostEmbeds} from '../util/post-embeds' -import {PostCtrls} from '../util/PostCtrls' +import {PostCtrls} from '../util/post-ctrls/PostCtrls' import {PostHider} from '../util/moderation/PostHider' import {ContentHider} from '../util/moderation/ContentHider' import {ImageHider} from '../util/moderation/ImageHider' diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index 0b49995fe..614c5ea77 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -20,7 +20,7 @@ import {Link} from '../util/Link' import {UserInfoText} from '../util/UserInfoText' import {PostMeta} from '../util/PostMeta' import {PostEmbeds} from '../util/post-embeds' -import {PostCtrls} from '../util/PostCtrls' +import {PostCtrls} from '../util/post-ctrls/PostCtrls' import {PostHider} from '../util/moderation/PostHider' import {ContentHider} from '../util/moderation/ContentHider' import {ImageHider} from '../util/moderation/ImageHider' diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 1084fb6fc..fa6131d61 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -13,7 +13,7 @@ import {Link, DesktopWebTextLink} from '../util/Link' import {Text} from '../util/text/Text' import {UserInfoText} from '../util/UserInfoText' import {PostMeta} from '../util/PostMeta' -import {PostCtrls} from '../util/PostCtrls' +import {PostCtrls} from '../util/post-ctrls/PostCtrls' import {PostEmbeds} from '../util/post-embeds' import {PostHider} from '../util/moderation/PostHider' import {ContentHider} from '../util/moderation/ContentHider' diff --git a/src/view/com/util/PostCtrls.tsx b/src/view/com/util/PostCtrls.tsx deleted file mode 100644 index 11b73cea0..000000000 --- a/src/view/com/util/PostCtrls.tsx +++ /dev/null @@ -1,321 +0,0 @@ -import React from 'react' -import { - StyleProp, - StyleSheet, - TouchableOpacity, - View, - ViewStyle, -} from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import ReactNativeHapticFeedback, { - HapticFeedbackTypes, -} from 'react-native-haptic-feedback' -// DISABLED see #135 -// import { -// TriggerableAnimated, -// TriggerableAnimatedRef, -// } from './anim/TriggerableAnimated' -import {Text} from './text/Text' -import {PostDropdownBtn} from './forms/DropdownButton' -import { - HeartIcon, - HeartIconSolid, - RepostIcon, - CommentBottomArrow, -} from 'lib/icons' -import {s, colors} from 'lib/styles' -import {useTheme} from 'lib/ThemeContext' -import {useStores} from 'state/index' -import {isIOS} from 'platform/detection' - -interface PostCtrlsOpts { - itemUri: string - itemCid: string - itemHref: string - itemTitle: string - isAuthor: boolean - author: { - handle: string - displayName: string - avatar: string - } - text: string - indexedAt: string - big?: boolean - style?: StyleProp - replyCount?: number - repostCount?: number - likeCount?: number - isReposted: boolean - isLiked: boolean - isThreadMuted: boolean - onPressReply: () => void - onPressToggleRepost: () => Promise - onPressToggleLike: () => Promise - onCopyPostText: () => void - onOpenTranslate: () => void - onToggleThreadMute: () => void - onDeletePost: () => void -} - -const HITSLOP = {top: 5, left: 5, bottom: 5, right: 5} -const hapticImpact: HapticFeedbackTypes = isIOS ? 'impactMedium' : 'impactLight' // Users said the medium impact was too strong on Android; see APP-537 - -// DISABLED see #135 -/* -function ctrlAnimStart(interp: Animated.Value) { - return Animated.sequence([ - Animated.timing(interp, { - toValue: 1, - duration: 250, - useNativeDriver: true, - }), - Animated.delay(50), - Animated.timing(interp, { - toValue: 0, - duration: 20, - useNativeDriver: true, - }), - ]) -} - -function ctrlAnimStyle(interp: Animated.Value) { - return { - transform: [ - { - scale: interp.interpolate({ - inputRange: [0, 1.0], - outputRange: [1.0, 4.0], - }), - }, - ], - opacity: interp.interpolate({ - inputRange: [0, 1.0], - outputRange: [1.0, 0.0], - }), - } -} -*/ - -export function PostCtrls(opts: PostCtrlsOpts) { - const store = useStores() - const theme = useTheme() - const defaultCtrlColor = React.useMemo( - () => ({ - color: theme.palette.default.postCtrl, - }), - [theme], - ) as StyleProp - // DISABLED see #135 - // const repostRef = React.useRef(null) - // const likeRef = React.useRef(null) - const onRepost = () => { - store.shell.closeModal() - if (!opts.isReposted) { - ReactNativeHapticFeedback.trigger(hapticImpact) - opts.onPressToggleRepost().catch(_e => undefined) - // DISABLED see #135 - // repostRef.current?.trigger( - // {start: ctrlAnimStart, style: ctrlAnimStyle}, - // async () => { - // await opts.onPressToggleRepost().catch(_e => undefined) - // setRepostMod(0) - // }, - // ) - } else { - opts.onPressToggleRepost().catch(_e => undefined) - } - } - - const onQuote = () => { - store.shell.closeModal() - store.shell.openComposer({ - quote: { - uri: opts.itemUri, - cid: opts.itemCid, - text: opts.text, - author: opts.author, - indexedAt: opts.indexedAt, - }, - }) - ReactNativeHapticFeedback.trigger(hapticImpact) - } - - const onPressToggleRepostWrapper = () => { - store.shell.openModal({ - name: 'repost', - onRepost: onRepost, - onQuote: onQuote, - isReposted: opts.isReposted, - }) - } - - const onPressToggleLikeWrapper = async () => { - if (!opts.isLiked) { - ReactNativeHapticFeedback.trigger(hapticImpact) - await opts.onPressToggleLike().catch(_e => undefined) - // DISABLED see #135 - // likeRef.current?.trigger( - // {start: ctrlAnimStart, style: ctrlAnimStyle}, - // async () => { - // await opts.onPressToggleLike().catch(_e => undefined) - // setLikeMod(0) - // }, - // ) - // setIsLikedPressed(false) - } else { - await opts.onPressToggleLike().catch(_e => undefined) - // setIsLikedPressed(false) - } - } - - return ( - - - - {typeof opts.replyCount !== 'undefined' ? ( - - {opts.replyCount} - - ) : undefined} - - - ) - : defaultCtrlColor - } - strokeWidth={2.4} - size={opts.big ? 24 : 20} - /> - {typeof opts.repostCount !== 'undefined' ? ( - - {opts.repostCount} - - ) : undefined} - - - {opts.isLiked ? ( - } - size={opts.big ? 22 : 16} - /> - ) : ( - - )} - {typeof opts.likeCount !== 'undefined' ? ( - - {opts.likeCount} - - ) : undefined} - - - {opts.big ? undefined : ( - - - - )} - - {/* used for adding pad to the right side */} - - - ) -} - -const styles = StyleSheet.create({ - ctrls: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - ctrl: { - flexDirection: 'row', - alignItems: 'center', - padding: 5, - margin: -5, - }, - ctrlIconReposted: { - color: colors.green3, - }, - ctrlIconLiked: { - color: colors.red3, - }, - mt1: { - marginTop: 1, - }, -}) diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx new file mode 100644 index 000000000..5c0296e28 --- /dev/null +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -0,0 +1,283 @@ +import React, {useCallback} from 'react' +import { + StyleProp, + StyleSheet, + TouchableOpacity, + View, + ViewStyle, +} from 'react-native' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import ReactNativeHapticFeedback, { + HapticFeedbackTypes, +} from 'react-native-haptic-feedback' +// DISABLED see #135 +// import { +// TriggerableAnimated, +// TriggerableAnimatedRef, +// } from './anim/TriggerableAnimated' +import {Text} from '../text/Text' +import {PostDropdownBtn} from '../forms/DropdownButton' +import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons' +import {s, colors} from 'lib/styles' +import {useTheme} from 'lib/ThemeContext' +import {useStores} from 'state/index' +import {isIOS, isNative} from 'platform/detection' +import {RepostButton} from './RepostButton' + +interface PostCtrlsOpts { + itemUri: string + itemCid: string + itemHref: string + itemTitle: string + isAuthor: boolean + author: { + handle: string + displayName: string + avatar: string + } + text: string + indexedAt: string + big?: boolean + style?: StyleProp + replyCount?: number + repostCount?: number + likeCount?: number + isReposted: boolean + isLiked: boolean + isThreadMuted: boolean + onPressReply: () => void + onPressToggleRepost: () => Promise + onPressToggleLike: () => Promise + onCopyPostText: () => void + onOpenTranslate: () => void + onToggleThreadMute: () => void + onDeletePost: () => void +} + +const HITSLOP = {top: 5, left: 5, bottom: 5, right: 5} +const hapticImpact: HapticFeedbackTypes = isIOS ? 'impactMedium' : 'impactLight' // Users said the medium impact was too strong on Android; see APP-537 + +// DISABLED see #135 +/* +function ctrlAnimStart(interp: Animated.Value) { + return Animated.sequence([ + Animated.timing(interp, { + toValue: 1, + duration: 250, + useNativeDriver: true, + }), + Animated.delay(50), + Animated.timing(interp, { + toValue: 0, + duration: 20, + useNativeDriver: true, + }), + ]) +} + +function ctrlAnimStyle(interp: Animated.Value) { + return { + transform: [ + { + scale: interp.interpolate({ + inputRange: [0, 1.0], + outputRange: [1.0, 4.0], + }), + }, + ], + opacity: interp.interpolate({ + inputRange: [0, 1.0], + outputRange: [1.0, 0.0], + }), + } +} +*/ + +export function PostCtrls(opts: PostCtrlsOpts) { + const store = useStores() + const theme = useTheme() + const defaultCtrlColor = React.useMemo( + () => ({ + color: theme.palette.default.postCtrl, + }), + [theme], + ) as StyleProp + // DISABLED see #135 + // const repostRef = React.useRef(null) + // const likeRef = React.useRef(null) + const onRepost = useCallback(() => { + store.shell.closeModal() + if (!opts.isReposted) { + if (isNative) { + ReactNativeHapticFeedback.trigger(hapticImpact) + } + opts.onPressToggleRepost().catch(_e => undefined) + // DISABLED see #135 + // repostRef.current?.trigger( + // {start: ctrlAnimStart, style: ctrlAnimStyle}, + // async () => { + // await opts.onPressToggleRepost().catch(_e => undefined) + // setRepostMod(0) + // }, + // ) + } else { + opts.onPressToggleRepost().catch(_e => undefined) + } + }, [opts, store.shell]) + + const onQuote = useCallback(() => { + store.shell.closeModal() + store.shell.openComposer({ + quote: { + uri: opts.itemUri, + cid: opts.itemCid, + text: opts.text, + author: opts.author, + indexedAt: opts.indexedAt, + }, + }) + + if (isNative) { + ReactNativeHapticFeedback.trigger(hapticImpact) + } + }, [ + opts.author, + opts.indexedAt, + opts.itemCid, + opts.itemUri, + opts.text, + store.shell, + ]) + + const onPressToggleLikeWrapper = async () => { + if (!opts.isLiked) { + ReactNativeHapticFeedback.trigger(hapticImpact) + await opts.onPressToggleLike().catch(_e => undefined) + // DISABLED see #135 + // likeRef.current?.trigger( + // {start: ctrlAnimStart, style: ctrlAnimStyle}, + // async () => { + // await opts.onPressToggleLike().catch(_e => undefined) + // setLikeMod(0) + // }, + // ) + // setIsLikedPressed(false) + } else { + await opts.onPressToggleLike().catch(_e => undefined) + // setIsLikedPressed(false) + } + } + + return ( + + + + {typeof opts.replyCount !== 'undefined' ? ( + + {opts.replyCount} + + ) : undefined} + + + + {opts.isLiked ? ( + } + size={opts.big ? 22 : 16} + /> + ) : ( + + )} + {typeof opts.likeCount !== 'undefined' ? ( + + {opts.likeCount} + + ) : undefined} + + + {opts.big ? undefined : ( + + + + )} + + {/* used for adding pad to the right side */} + + + ) +} + +const styles = StyleSheet.create({ + ctrls: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + ctrl: { + flexDirection: 'row', + alignItems: 'center', + padding: 5, + margin: -5, + }, + ctrlIconLiked: { + color: colors.red3, + }, + mt1: { + marginTop: 1, + }, +}) diff --git a/src/view/com/util/post-ctrls/RepostButton.tsx b/src/view/com/util/post-ctrls/RepostButton.tsx new file mode 100644 index 000000000..e6de4cb19 --- /dev/null +++ b/src/view/com/util/post-ctrls/RepostButton.tsx @@ -0,0 +1,95 @@ +import React, {useCallback} from 'react' +import {StyleProp, StyleSheet, TouchableOpacity, ViewStyle} from 'react-native' +import {RepostIcon} from 'lib/icons' +import {s, colors} from 'lib/styles' +import {useTheme} from 'lib/ThemeContext' +import {Text} from '../text/Text' +import {useStores} from 'state/index' + +const HITSLOP = {top: 5, left: 5, bottom: 5, right: 5} + +interface Props { + isReposted: boolean + repostCount?: number + big?: boolean + onRepost: () => void + onQuote: () => void +} + +export const RepostButton = ({ + isReposted, + repostCount, + big, + onRepost, + onQuote, +}: Props) => { + const store = useStores() + const theme = useTheme() + + const defaultControlColor = React.useMemo( + () => ({ + color: theme.palette.default.postCtrl, + }), + [theme], + ) + + const onPressToggleRepostWrapper = useCallback(() => { + store.shell.openModal({ + name: 'repost', + onRepost: onRepost, + onQuote: onQuote, + isReposted, + }) + }, [onRepost, onQuote, isReposted, store.shell]) + + return ( + + ) + : defaultControlColor + } + strokeWidth={2.4} + size={big ? 24 : 20} + /> + {typeof repostCount !== 'undefined' ? ( + + {repostCount} + + ) : undefined} + + ) +} + +const styles = StyleSheet.create({ + control: { + flexDirection: 'row', + alignItems: 'center', + padding: 5, + margin: -5, + }, + reposted: { + color: colors.green3, + }, + repostCount: { + color: 'currentColor', + }, +}) diff --git a/src/view/com/util/post-ctrls/RepostButton.web.tsx b/src/view/com/util/post-ctrls/RepostButton.web.tsx new file mode 100644 index 000000000..66cc0d123 --- /dev/null +++ b/src/view/com/util/post-ctrls/RepostButton.web.tsx @@ -0,0 +1,86 @@ +import React, {useMemo} from 'react' +import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' +import {RepostIcon} from 'lib/icons' +import {DropdownButton} from '../forms/DropdownButton' +import {colors} from 'lib/styles' +import {useTheme} from 'lib/ThemeContext' +import {Text} from '../text/Text' + +interface Props { + isReposted: boolean + repostCount?: number + big?: boolean + onRepost: () => void + onQuote: () => void +} + +export const RepostButton = ({ + isReposted, + repostCount, + big, + onRepost, + onQuote, +}: Props) => { + const theme = useTheme() + + const defaultControlColor = React.useMemo( + () => ({ + color: theme.palette.default.postCtrl, + }), + [theme], + ) + + const items = useMemo( + () => [ + { + label: isReposted ? 'Undo repost' : 'Repost', + icon: 'retweet' as const, + onPress: onRepost, + }, + {label: 'Quote post', icon: 'quote-left' as const, onPress: onQuote}, + ], + [isReposted, onRepost, onQuote], + ) + + return ( + + , + ]}> + + {typeof repostCount !== 'undefined' ? ( + + {repostCount ?? 0} + + ) : undefined} + + + ) +} + +const styles = StyleSheet.create({ + control: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + reposted: { + color: colors.green3, + }, + repostCount: { + color: 'currentColor', + }, +}) -- cgit 1.4.1