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 <pfrazee@gmail.com>
zio/stable
Ollie H 2023-05-15 13:18:39 -07:00 committed by GitHub
parent 0a0afdf2c2
commit 628d877325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 210 additions and 67 deletions

View File

@ -21,7 +21,7 @@ import {pluralize} from 'lib/strings/helpers'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {PostMeta} from '../util/PostMeta' import {PostMeta} from '../util/PostMeta'
import {PostEmbeds} from '../util/post-embeds' 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 {PostHider} from '../util/moderation/PostHider'
import {ContentHider} from '../util/moderation/ContentHider' import {ContentHider} from '../util/moderation/ContentHider'
import {ImageHider} from '../util/moderation/ImageHider' import {ImageHider} from '../util/moderation/ImageHider'

View File

@ -20,7 +20,7 @@ import {Link} from '../util/Link'
import {UserInfoText} from '../util/UserInfoText' import {UserInfoText} from '../util/UserInfoText'
import {PostMeta} from '../util/PostMeta' import {PostMeta} from '../util/PostMeta'
import {PostEmbeds} from '../util/post-embeds' 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 {PostHider} from '../util/moderation/PostHider'
import {ContentHider} from '../util/moderation/ContentHider' import {ContentHider} from '../util/moderation/ContentHider'
import {ImageHider} from '../util/moderation/ImageHider' import {ImageHider} from '../util/moderation/ImageHider'

View File

@ -13,7 +13,7 @@ import {Link, DesktopWebTextLink} from '../util/Link'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {UserInfoText} from '../util/UserInfoText' import {UserInfoText} from '../util/UserInfoText'
import {PostMeta} from '../util/PostMeta' import {PostMeta} from '../util/PostMeta'
import {PostCtrls} from '../util/PostCtrls' import {PostCtrls} from '../util/post-ctrls/PostCtrls'
import {PostEmbeds} from '../util/post-embeds' import {PostEmbeds} from '../util/post-embeds'
import {PostHider} from '../util/moderation/PostHider' import {PostHider} from '../util/moderation/PostHider'
import {ContentHider} from '../util/moderation/ContentHider' import {ContentHider} from '../util/moderation/ContentHider'

View File

@ -1,4 +1,4 @@
import React from 'react' import React, {useCallback} from 'react'
import { import {
StyleProp, StyleProp,
StyleSheet, StyleSheet,
@ -18,18 +18,14 @@ import ReactNativeHapticFeedback, {
// TriggerableAnimated, // TriggerableAnimated,
// TriggerableAnimatedRef, // TriggerableAnimatedRef,
// } from './anim/TriggerableAnimated' // } from './anim/TriggerableAnimated'
import {Text} from './text/Text' import {Text} from '../text/Text'
import {PostDropdownBtn} from './forms/DropdownButton' import {PostDropdownBtn} from '../forms/DropdownButton'
import { import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
HeartIcon,
HeartIconSolid,
RepostIcon,
CommentBottomArrow,
} from 'lib/icons'
import {s, colors} from 'lib/styles' import {s, colors} from 'lib/styles'
import {useTheme} from 'lib/ThemeContext' import {useTheme} from 'lib/ThemeContext'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {isIOS} from 'platform/detection' import {isIOS, isNative} from 'platform/detection'
import {RepostButton} from './RepostButton'
interface PostCtrlsOpts { interface PostCtrlsOpts {
itemUri: string itemUri: string
@ -112,10 +108,12 @@ export function PostCtrls(opts: PostCtrlsOpts) {
// DISABLED see #135 // DISABLED see #135
// const repostRef = React.useRef<TriggerableAnimatedRef | null>(null) // const repostRef = React.useRef<TriggerableAnimatedRef | null>(null)
// const likeRef = React.useRef<TriggerableAnimatedRef | null>(null) // const likeRef = React.useRef<TriggerableAnimatedRef | null>(null)
const onRepost = () => { const onRepost = useCallback(() => {
store.shell.closeModal() store.shell.closeModal()
if (!opts.isReposted) { if (!opts.isReposted) {
if (isNative) {
ReactNativeHapticFeedback.trigger(hapticImpact) ReactNativeHapticFeedback.trigger(hapticImpact)
}
opts.onPressToggleRepost().catch(_e => undefined) opts.onPressToggleRepost().catch(_e => undefined)
// DISABLED see #135 // DISABLED see #135
// repostRef.current?.trigger( // repostRef.current?.trigger(
@ -128,9 +126,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
} else { } else {
opts.onPressToggleRepost().catch(_e => undefined) opts.onPressToggleRepost().catch(_e => undefined)
} }
} }, [opts, store.shell])
const onQuote = () => { const onQuote = useCallback(() => {
store.shell.closeModal() store.shell.closeModal()
store.shell.openComposer({ store.shell.openComposer({
quote: { quote: {
@ -141,17 +139,18 @@ export function PostCtrls(opts: PostCtrlsOpts) {
indexedAt: opts.indexedAt, indexedAt: opts.indexedAt,
}, },
}) })
if (isNative) {
ReactNativeHapticFeedback.trigger(hapticImpact) ReactNativeHapticFeedback.trigger(hapticImpact)
} }
}, [
const onPressToggleRepostWrapper = () => { opts.author,
store.shell.openModal({ opts.indexedAt,
name: 'repost', opts.itemCid,
onRepost: onRepost, opts.itemUri,
onQuote: onQuote, opts.text,
isReposted: opts.isReposted, store.shell,
}) ])
}
const onPressToggleLikeWrapper = async () => { const onPressToggleLikeWrapper = async () => {
if (!opts.isLiked) { if (!opts.isLiked) {
@ -181,7 +180,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
onPress={opts.onPressReply} onPress={opts.onPressReply}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel="Reply" accessibilityLabel="Reply"
accessibilityHint="Opens reply composer"> accessibilityHint="reply composer">
<CommentBottomArrow <CommentBottomArrow
style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]} style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]}
strokeWidth={3} strokeWidth={3}
@ -193,39 +192,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
</Text> </Text>
) : undefined} ) : undefined}
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <RepostButton {...opts} onRepost={onRepost} onQuote={onQuote} />
testID="repostBtn"
hitSlop={HITSLOP}
onPress={onPressToggleRepostWrapper}
style={styles.ctrl}
accessibilityRole="button"
accessibilityLabel={opts.isReposted ? 'Undo repost' : 'Repost'}
accessibilityHint={
opts.isReposted
? `Remove your repost of ${opts.author}'s post`
: `Repost or quote post ${opts.author}'s post`
}>
<RepostIcon
style={
opts.isReposted
? (styles.ctrlIconReposted as StyleProp<ViewStyle>)
: defaultCtrlColor
}
strokeWidth={2.4}
size={opts.big ? 24 : 20}
/>
{typeof opts.repostCount !== 'undefined' ? (
<Text
testID="repostCount"
style={
opts.isReposted
? [s.bold, s.green3, s.f15, s.ml5]
: [defaultCtrlColor, s.f15, s.ml5]
}>
{opts.repostCount}
</Text>
) : undefined}
</TouchableOpacity>
<TouchableOpacity <TouchableOpacity
testID="likeBtn" testID="likeBtn"
style={styles.ctrl} style={styles.ctrl}
@ -234,9 +201,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={opts.isLiked ? 'Unlike' : 'Like'} accessibilityLabel={opts.isLiked ? 'Unlike' : 'Like'}
accessibilityHint={ accessibilityHint={
opts.isReposted opts.isReposted ? `Removes like from the post` : `Like the post`
? `Removes like from ${opts.author}'s post`
: `Like ${opts.author}'s post`
}> }>
{opts.isLiked ? ( {opts.isLiked ? (
<HeartIconSolid <HeartIconSolid
@ -309,9 +274,6 @@ const styles = StyleSheet.create({
padding: 5, padding: 5,
margin: -5, margin: -5,
}, },
ctrlIconReposted: {
color: colors.green3,
},
ctrlIconLiked: { ctrlIconLiked: {
color: colors.red3, color: colors.red3,
}, },

View File

@ -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 (
<TouchableOpacity
testID="repostBtn"
hitSlop={HITSLOP}
onPress={onPressToggleRepostWrapper}
style={styles.control}
accessibilityRole="button"
accessibilityLabel={isReposted ? 'Undo repost' : 'Repost'}
accessibilityHint={
isReposted
? `Remove your repost of the post`
: `Repost or quote post the post`
}>
<RepostIcon
style={
isReposted
? (styles.reposted as StyleProp<ViewStyle>)
: defaultControlColor
}
strokeWidth={2.4}
size={big ? 24 : 20}
/>
{typeof repostCount !== 'undefined' ? (
<Text
testID="repostCount"
style={
isReposted
? [s.bold, s.green3, s.f15, s.ml5]
: [defaultControlColor, s.f15, s.ml5]
}>
{repostCount}
</Text>
) : undefined}
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
control: {
flexDirection: 'row',
alignItems: 'center',
padding: 5,
margin: -5,
},
reposted: {
color: colors.green3,
},
repostCount: {
color: 'currentColor',
},
})

View File

@ -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 (
<DropdownButton
type="bare"
items={items}
bottomOffset={4}
openToRight
rightOffset={-40}>
<View
style={[
styles.control,
(isReposted
? styles.reposted
: defaultControlColor) as StyleProp<ViewStyle>,
]}>
<RepostIcon strokeWidth={2.4} size={big ? 24 : 20} />
{typeof repostCount !== 'undefined' ? (
<Text
testID="repostCount"
type={isReposted ? 'md-bold' : 'md-medium'}
style={styles.repostCount}>
{repostCount ?? 0}
</Text>
) : undefined}
</View>
</DropdownButton>
)
}
const styles = StyleSheet.create({
control: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: 4,
},
reposted: {
color: colors.green3,
},
repostCount: {
color: 'currentColor',
},
})