* fix comments * add zeego package * get basic native dropdown working * add separator and icon components * refined native dropdown component * add android build properties to app.json * move `PostDropdownBtn` to its own component * fix selectors issue * move `PostDropdownBtn` to its own component * fix hitslop * fix post dropdown hitslop * fix android dropdown icons * move `UserAvatar.tsx` to native dropdown * use native dropdown in `ProfileHeader.tsx` * use native dropdown in `PostThreadItem.tsx` * use native dropdown in `UserBanner.tsx` * use native dropdown in `CustomFeed.tsx` * replace `testId` with `testID` (which is what is used everywhere) * move `Settings.tsx` to use native dropdown * create jest mocks for zeego * create jest mock for `zeego/dropdown-menu` * web styles for native dropdown * remove example native dropdown * adjust web styles * fix propagation * fix pressable in `Settings.tsx` * animate dropdown on web * add keyboard nav and hover styles * add hitslop to constants * add comments to NativeDropdown component * temporarily removed android icons * add testID to PostDropdownBtn * add testID back to all NativeDropdown button implementations * add postDropdownBtn testID * add testID to dropdown items * remove testID from dropdown menu item * refactor home-screen tests for native dropdown * refactor profile-screen tests for native dropdown * refactor thread-muting tests for native dropdown * refactor thread-screen tests for native dropdown * fix dropdown color for post dropdown button * remove icons from android dropdown menu * fix `create-account.test.ts` * fix `invite-codes.test.ts`
260 lines
6.7 KiB
TypeScript
260 lines
6.7 KiB
TypeScript
import React, {useCallback} from 'react'
|
|
import {
|
|
StyleProp,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
View,
|
|
ViewStyle,
|
|
} from 'react-native'
|
|
// DISABLED see #135
|
|
// import {
|
|
// TriggerableAnimated,
|
|
// TriggerableAnimatedRef,
|
|
// } from './anim/TriggerableAnimated'
|
|
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 {createHitslop} from 'lib/constants'
|
|
|
|
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
|
|
big?: boolean
|
|
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
|
|
}
|
|
|
|
const HITSLOP = createHitslop(5)
|
|
|
|
// 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<ViewStyle>
|
|
// DISABLED see #135
|
|
// const repostRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
|
// const likeRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
|
const onRepost = useCallback(() => {
|
|
store.shell.closeModal()
|
|
if (!opts.isReposted) {
|
|
Haptics.default()
|
|
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,
|
|
},
|
|
})
|
|
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)
|
|
// 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 (
|
|
<View style={[styles.ctrls, opts.style]}>
|
|
<TouchableOpacity
|
|
testID="replyBtn"
|
|
style={styles.ctrl}
|
|
hitSlop={HITSLOP}
|
|
onPress={opts.onPressReply}
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`Reply (${opts.replyCount} ${
|
|
opts.replyCount === 1 ? 'reply' : 'replies'
|
|
})`}
|
|
accessibilityHint="reply composer">
|
|
<CommentBottomArrow
|
|
style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]}
|
|
strokeWidth={3}
|
|
size={opts.big ? 20 : 15}
|
|
/>
|
|
{typeof opts.replyCount !== 'undefined' ? (
|
|
<Text style={[defaultCtrlColor, s.ml5, s.f15]}>
|
|
{opts.replyCount}
|
|
</Text>
|
|
) : undefined}
|
|
</TouchableOpacity>
|
|
<RepostButton {...opts} onRepost={onRepost} onQuote={onQuote} />
|
|
<TouchableOpacity
|
|
testID="likeBtn"
|
|
style={styles.ctrl}
|
|
hitSlop={HITSLOP}
|
|
onPress={onPressToggleLikeWrapper}
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`${opts.isLiked ? 'Unlike' : 'Like'} (${
|
|
opts.likeCount
|
|
} ${pluralize(opts.likeCount || 0, 'like')})`}
|
|
accessibilityHint="">
|
|
{opts.isLiked ? (
|
|
<HeartIconSolid
|
|
style={styles.ctrlIconLiked}
|
|
size={opts.big ? 22 : 16}
|
|
/>
|
|
) : (
|
|
<HeartIcon
|
|
style={[defaultCtrlColor, opts.big ? styles.mt1 : undefined]}
|
|
strokeWidth={3}
|
|
size={opts.big ? 20 : 16}
|
|
/>
|
|
)}
|
|
{typeof opts.likeCount !== 'undefined' ? (
|
|
<Text
|
|
testID="likeCount"
|
|
style={
|
|
opts.isLiked
|
|
? [s.bold, s.red3, s.f15, s.ml5]
|
|
: [defaultCtrlColor, s.f15, s.ml5]
|
|
}>
|
|
{opts.likeCount}
|
|
</Text>
|
|
) : undefined}
|
|
</TouchableOpacity>
|
|
{opts.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}
|
|
/>
|
|
)}
|
|
{/* used for adding pad to the right side */}
|
|
<View />
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
ctrls: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
},
|
|
ctrl: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
padding: 5,
|
|
margin: -5,
|
|
},
|
|
ctrlIconLiked: {
|
|
color: colors.like,
|
|
},
|
|
mt1: {
|
|
marginTop: 1,
|
|
},
|
|
})
|