Merge branch 'main' into custom-algos
This commit is contained in:
commit
7aa1d9010e
99 changed files with 4234 additions and 716 deletions
|
@ -14,7 +14,8 @@ export const BlurView = ({
|
|||
...props
|
||||
}: React.PropsWithChildren<BlurViewProps>) => {
|
||||
// @ts-ignore using an RNW-specific attribute here -prf
|
||||
style = addStyle(style, {backdropFilter: `blur(${blurAmount || 10}px`})
|
||||
let blur = `blur(${blurAmount || 10}px`
|
||||
style = addStyle(style, {backdropFilter: blur, WebkitBackdropFilter: blur})
|
||||
if (blurType === 'dark') {
|
||||
style = addStyle(style, styles.dark)
|
||||
} else {
|
||||
|
|
|
@ -10,17 +10,19 @@ import {UserGroupIcon} from 'lib/icons'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
|
||||
export function EmptyState({
|
||||
testID,
|
||||
icon,
|
||||
message,
|
||||
style,
|
||||
}: {
|
||||
testID?: string
|
||||
icon: IconProp | 'user-group'
|
||||
message: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
<View testID={testID} style={[styles.container, style]}>
|
||||
<View style={styles.iconContainer}>
|
||||
{icon === 'user-group' ? (
|
||||
<UserGroupIcon size="64" style={styles.icon} />
|
||||
|
|
88
src/view/com/util/EmptyStateWithButton.tsx
Normal file
88
src/view/com/util/EmptyStateWithButton.tsx
Normal file
|
@ -0,0 +1,88 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconStyle,
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
||||
import {Text} from './text/Text'
|
||||
import {Button} from './forms/Button'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
|
||||
interface Props {
|
||||
testID?: string
|
||||
icon: IconProp
|
||||
message: string
|
||||
buttonLabel: string
|
||||
onPress: () => void
|
||||
}
|
||||
|
||||
export function EmptyStateWithButton(props: Props) {
|
||||
const pal = usePalette('default')
|
||||
const palInverted = usePalette('inverted')
|
||||
|
||||
return (
|
||||
<View testID={props.testID} style={styles.container}>
|
||||
<View style={styles.iconContainer}>
|
||||
<FontAwesomeIcon
|
||||
icon={props.icon}
|
||||
style={[styles.icon, pal.text]}
|
||||
size={62}
|
||||
/>
|
||||
</View>
|
||||
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||
{props.message}
|
||||
</Text>
|
||||
<View style={styles.btns}>
|
||||
<Button
|
||||
testID={props.testID ? `${props.testID}-button` : undefined}
|
||||
type="inverted"
|
||||
style={styles.btn}
|
||||
onPress={props.onPress}>
|
||||
<FontAwesomeIcon
|
||||
icon="plus"
|
||||
style={palInverted.text as FontAwesomeIconStyle}
|
||||
size={14}
|
||||
/>
|
||||
<Text type="lg-medium" style={palInverted.text}>
|
||||
{props.buttonLabel}
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
height: '100%',
|
||||
paddingVertical: 40,
|
||||
paddingHorizontal: 30,
|
||||
},
|
||||
iconContainer: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
icon: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
btns: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
btn: {
|
||||
gap: 10,
|
||||
marginVertical: 20,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 24,
|
||||
borderRadius: 30,
|
||||
},
|
||||
notice: {
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
marginHorizontal: 30,
|
||||
},
|
||||
})
|
|
@ -66,6 +66,7 @@ export function UserAvatar({
|
|||
if (!(await requestCameraAccessIfNeeded())) {
|
||||
return
|
||||
}
|
||||
|
||||
onSelectNewAvatar?.(
|
||||
await openCamera(store, {
|
||||
width: 1000,
|
||||
|
@ -83,20 +84,21 @@ export function UserAvatar({
|
|||
if (!(await requestPhotoAccessIfNeeded())) {
|
||||
return
|
||||
}
|
||||
|
||||
const items = await openPicker(store, {
|
||||
aspect: [1, 1],
|
||||
})
|
||||
const item = items[0]
|
||||
|
||||
const croppedImage = await openCropper(store, {
|
||||
mediaType: 'photo',
|
||||
multiple: false,
|
||||
cropperCircleOverlay: true,
|
||||
height: item.height,
|
||||
width: item.width,
|
||||
path: item.path,
|
||||
})
|
||||
|
||||
onSelectNewAvatar?.(
|
||||
await openCropper(store, {
|
||||
mediaType: 'photo',
|
||||
path: items[0].path,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
cropperCircleOverlay: true,
|
||||
}),
|
||||
)
|
||||
onSelectNewAvatar?.(croppedImage)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -55,10 +55,8 @@ export function UserBanner({
|
|||
if (!(await requestPhotoAccessIfNeeded())) {
|
||||
return
|
||||
}
|
||||
const items = await openPicker(store, {
|
||||
mediaType: 'photo',
|
||||
multiple: false,
|
||||
})
|
||||
const items = await openPicker(store)
|
||||
|
||||
onSelectNewBanner?.(
|
||||
await openCropper(store, {
|
||||
mediaType: 'photo',
|
||||
|
|
|
@ -20,11 +20,13 @@ export const ViewHeader = observer(function ({
|
|||
canGoBack,
|
||||
hideOnScroll,
|
||||
showOnDesktop,
|
||||
renderButton,
|
||||
}: {
|
||||
title: string
|
||||
canGoBack?: boolean
|
||||
hideOnScroll?: boolean
|
||||
showOnDesktop?: boolean
|
||||
renderButton?: () => JSX.Element
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
|
@ -46,7 +48,7 @@ export const ViewHeader = observer(function ({
|
|||
|
||||
if (isDesktopWeb) {
|
||||
if (showOnDesktop) {
|
||||
return <DesktopWebHeader title={title} />
|
||||
return <DesktopWebHeader title={title} renderButton={renderButton} />
|
||||
}
|
||||
return null
|
||||
} else {
|
||||
|
@ -79,13 +81,23 @@ export const ViewHeader = observer(function ({
|
|||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
|
||||
{renderButton ? (
|
||||
renderButton()
|
||||
) : (
|
||||
<View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function DesktopWebHeader({title}: {title: string}) {
|
||||
function DesktopWebHeader({
|
||||
title,
|
||||
renderButton,
|
||||
}: {
|
||||
title: string
|
||||
renderButton?: () => JSX.Element
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
return (
|
||||
<CenteredView style={[styles.header, styles.desktopHeader, pal.border]}>
|
||||
|
@ -94,6 +106,7 @@ function DesktopWebHeader({title}: {title: string}) {
|
|||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
{renderButton?.()}
|
||||
</CenteredView>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
View,
|
||||
ViewProps,
|
||||
} from 'react-native'
|
||||
import {addStyle, colors} from 'lib/styles'
|
||||
import {addStyle} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
|
||||
interface AddedProps {
|
||||
|
@ -124,12 +124,6 @@ const styles = StyleSheet.create({
|
|||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
containerLight: {
|
||||
backgroundColor: colors.gray1,
|
||||
},
|
||||
containerDark: {
|
||||
backgroundColor: colors.gray7,
|
||||
},
|
||||
fixedHeight: {
|
||||
height: '100vh',
|
||||
},
|
||||
|
|
|
@ -38,6 +38,7 @@ export function Button({
|
|||
accessibilityLabel,
|
||||
accessibilityHint,
|
||||
accessibilityLabelledBy,
|
||||
onAccessibilityEscape,
|
||||
}: React.PropsWithChildren<{
|
||||
type?: ButtonType
|
||||
label?: string
|
||||
|
@ -48,6 +49,7 @@ export function Button({
|
|||
accessibilityLabel?: string
|
||||
accessibilityHint?: string
|
||||
accessibilityLabelledBy?: string
|
||||
onAccessibilityEscape?: () => void
|
||||
}>) {
|
||||
const theme = useTheme()
|
||||
const typeOuterStyle = choose<ViewStyle, Record<ButtonType, ViewStyle>>(
|
||||
|
@ -126,6 +128,7 @@ export function Button({
|
|||
},
|
||||
},
|
||||
)
|
||||
|
||||
const onPressWrapped = React.useCallback(
|
||||
(event: Event) => {
|
||||
event.stopPropagation()
|
||||
|
@ -134,15 +137,30 @@ export function Button({
|
|||
},
|
||||
[onPress],
|
||||
)
|
||||
|
||||
const getStyle = React.useCallback(
|
||||
state => {
|
||||
const arr = [typeOuterStyle, styles.outer, style]
|
||||
if (state.pressed) {
|
||||
arr.push({opacity: 0.6})
|
||||
} else if (state.hovered) {
|
||||
arr.push({opacity: 0.8})
|
||||
}
|
||||
return arr
|
||||
},
|
||||
[typeOuterStyle, style],
|
||||
)
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={[typeOuterStyle, styles.outer, style]}
|
||||
style={getStyle}
|
||||
onPress={onPressWrapped}
|
||||
testID={testID}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityHint={accessibilityHint}
|
||||
accessibilityLabelledBy={accessibilityLabelledBy}>
|
||||
accessibilityLabelledBy={accessibilityLabelledBy}
|
||||
onAccessibilityEscape={onAccessibilityEscape}>
|
||||
{label ? (
|
||||
<Text type="button" style={[typeLabelStyle, labelStyle]}>
|
||||
{label}
|
||||
|
|
|
@ -209,7 +209,7 @@ export function PostDropdownBtn({
|
|||
},
|
||||
},
|
||||
{sep: true},
|
||||
{
|
||||
!isAuthor && {
|
||||
testID: 'postDropdownReportBtn',
|
||||
icon: 'circle-exclamation',
|
||||
label: 'Report post',
|
||||
|
@ -339,7 +339,9 @@ const DropdownItems = ({
|
|||
color={pal.text.color as string}
|
||||
/>
|
||||
)}
|
||||
<Text style={[styles.label, pal.text]}>{item.label}</Text>
|
||||
<Text style={[styles.label, pal.text]} numberOfLines={1}>
|
||||
{item.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
} else if (isSep(item)) {
|
||||
|
|
|
@ -63,6 +63,5 @@ const styles = StyleSheet.create({
|
|||
position: 'absolute',
|
||||
left: 6,
|
||||
bottom: 6,
|
||||
width: 46,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react'
|
||||
import React, {useCallback} from 'react'
|
||||
import {
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
|
@ -18,18 +18,14 @@ import ReactNativeHapticFeedback, {
|
|||
// TriggerableAnimated,
|
||||
// TriggerableAnimatedRef,
|
||||
// } from './anim/TriggerableAnimated'
|
||||
import {Text} from './text/Text'
|
||||
import {PostDropdownBtn} from './forms/DropdownButton'
|
||||
import {
|
||||
HeartIcon,
|
||||
HeartIconSolid,
|
||||
RepostIcon,
|
||||
CommentBottomArrow,
|
||||
} from 'lib/icons'
|
||||
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} from 'platform/detection'
|
||||
import {isIOS, isNative} from 'platform/detection'
|
||||
import {RepostButton} from './RepostButton'
|
||||
|
||||
interface PostCtrlsOpts {
|
||||
itemUri: string
|
||||
|
@ -112,10 +108,12 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
// DISABLED see #135
|
||||
// const repostRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
||||
// const likeRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
||||
const onRepost = () => {
|
||||
const onRepost = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
if (!opts.isReposted) {
|
||||
ReactNativeHapticFeedback.trigger(hapticImpact)
|
||||
if (isNative) {
|
||||
ReactNativeHapticFeedback.trigger(hapticImpact)
|
||||
}
|
||||
opts.onPressToggleRepost().catch(_e => undefined)
|
||||
// DISABLED see #135
|
||||
// repostRef.current?.trigger(
|
||||
|
@ -128,9 +126,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
} else {
|
||||
opts.onPressToggleRepost().catch(_e => undefined)
|
||||
}
|
||||
}
|
||||
}, [opts, store.shell])
|
||||
|
||||
const onQuote = () => {
|
||||
const onQuote = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
store.shell.openComposer({
|
||||
quote: {
|
||||
|
@ -141,17 +139,18 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
indexedAt: opts.indexedAt,
|
||||
},
|
||||
})
|
||||
ReactNativeHapticFeedback.trigger(hapticImpact)
|
||||
}
|
||||
|
||||
const onPressToggleRepostWrapper = () => {
|
||||
store.shell.openModal({
|
||||
name: 'repost',
|
||||
onRepost: onRepost,
|
||||
onQuote: onQuote,
|
||||
isReposted: opts.isReposted,
|
||||
})
|
||||
}
|
||||
if (isNative) {
|
||||
ReactNativeHapticFeedback.trigger(hapticImpact)
|
||||
}
|
||||
}, [
|
||||
opts.author,
|
||||
opts.indexedAt,
|
||||
opts.itemCid,
|
||||
opts.itemUri,
|
||||
opts.text,
|
||||
store.shell,
|
||||
])
|
||||
|
||||
const onPressToggleLikeWrapper = async () => {
|
||||
if (!opts.isLiked) {
|
||||
|
@ -181,7 +180,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
onPress={opts.onPressReply}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Reply"
|
||||
accessibilityHint="Opens reply composer">
|
||||
accessibilityHint="reply composer">
|
||||
<CommentBottomArrow
|
||||
style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]}
|
||||
strokeWidth={3}
|
||||
|
@ -193,39 +192,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
</Text>
|
||||
) : undefined}
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
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>
|
||||
<RepostButton {...opts} onRepost={onRepost} onQuote={onQuote} />
|
||||
<TouchableOpacity
|
||||
testID="likeBtn"
|
||||
style={styles.ctrl}
|
||||
|
@ -234,9 +201,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
accessibilityRole="button"
|
||||
accessibilityLabel={opts.isLiked ? 'Unlike' : 'Like'}
|
||||
accessibilityHint={
|
||||
opts.isReposted
|
||||
? `Removes like from ${opts.author}'s post`
|
||||
: `Like ${opts.author}'s post`
|
||||
opts.isReposted ? `Removes like from the post` : `Like the post`
|
||||
}>
|
||||
{opts.isLiked ? (
|
||||
<HeartIconSolid
|
||||
|
@ -309,9 +274,6 @@ const styles = StyleSheet.create({
|
|||
padding: 5,
|
||||
margin: -5,
|
||||
},
|
||||
ctrlIconReposted: {
|
||||
color: colors.green3,
|
||||
},
|
||||
ctrlIconLiked: {
|
||||
color: colors.red3,
|
||||
},
|
95
src/view/com/util/post-ctrls/RepostButton.tsx
Normal file
95
src/view/com/util/post-ctrls/RepostButton.tsx
Normal 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',
|
||||
},
|
||||
})
|
86
src/view/com/util/post-ctrls/RepostButton.web.tsx
Normal file
86
src/view/com/util/post-ctrls/RepostButton.web.tsx
Normal 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',
|
||||
},
|
||||
})
|
|
@ -210,6 +210,5 @@ const styles = StyleSheet.create({
|
|||
position: 'absolute',
|
||||
left: 6,
|
||||
bottom: 6,
|
||||
width: 46,
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue