Use ALF for post controls (#3400)

* alf the repost dropdown on web + import icons

* alf like icon

* convert other post controls

* add missing padding to share button

* refine buttons and use better icons

* revert buttonicon changes

* remove ButtonIcon and ButtonText from repost dialog

* use 15px font size when not big

* reduce size and use contrast_25

* add hover state to logged out view

* add `userSelect: 'none'` to buttons

* use width rather than height

* fix quote close behaviour

* prettier

* Fix Esc on repost

* Use new icons for placeholder

* Fix placeholder

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
zio/stable
Samuel Newman 2024-05-30 03:25:11 +03:00 committed by GitHub
parent 4d39ef2e19
commit 165feedb86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 376 additions and 285 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M3.004 4a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2v5a1 1 0 0 0 .43.822c.57.395 1.176.031 1.685-.255.428-.24.998-.614 1.569-1.15 1.154-1.082 2.316-2.832 2.316-5.417V5a1 1 0 0 0-1-1h-7ZM14.004 4a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2v5a1 1 0 0 0 .43.822c.57.395 1.176.031 1.685-.255.428-.24.998-.614 1.569-1.15 1.154-1.082 2.316-2.832 2.316-5.417V5a1 1 0 0 0-1-1h-7Z"/></svg>

After

Width:  |  Height:  |  Size: 450 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M2.004 5a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v8c0 2.585-1.162 4.335-2.316 5.417-.571.536-1.14.91-1.569 1.15a7.01 7.01 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001L6.004 19l.351.936A1 1 0 0 1 5.004 19v-5h-2a1 1 0 0 1-1-1V5Zm5 12.234c.104-.085.21-.177.316-.276.846-.793 1.684-2.043 1.684-3.958V6h-5v6h2a1 1 0 0 1 1 1v4.234Zm6-12.234a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v8c0 2.585-1.162 4.335-2.316 5.417-.571.536-1.14.91-1.569 1.15a7.018 7.018 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001-.352-.936.351.936A1 1 0 0 1 16.004 19v-5h-2a1 1 0 0 1-1-1V5Zm5 12.234V13a1 1 0 0 0-1-1h-2V6h5v7c0 1.915-.838 3.165-1.684 3.958-.106.1-.212.191-.316.276Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 775 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M2.003 5.999a2 2 0 0 1 2-1.999h5c1.104 0 2 .893 2 1.999V13c0 2.585-1.16 4.335-2.315 5.417-.571.536-1.14.91-1.569 1.15a7.01 7.01 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001L6.004 19l.351.936a1 1 0 0 1-1.351-.935L5 14H4a2 2 0 0 1-2-2.001l.002-6Zm5 11.236L7 12.999A1 1 0 0 0 6 12H4l.003-6h5v7c0 1.915-.837 3.165-1.683 3.958-.106.1-.213.192-.317.277Zm6-11.235a2 2 0 0 1 2-2h5c1.104 0 2 .893 2 1.999V13c0 2.585-1.16 4.335-2.315 5.417-.571.536-1.14.91-1.569 1.15a7.018 7.018 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001-.352-.936.351.936A1 1 0 0 1 16.004 19v-5h-1a2 2 0 0 1-2-2V6Zm7 0h-5v6h2a1 1 0 0 1 1 1v4.234c.105-.085.211-.177.317-.276.846-.793 1.684-2.043 1.684-3.958V6Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M8.004 5a1 1 0 0 0-.43-.822c-.57-.395-1.176-.031-1.685.255-.428.24-.998.614-1.569 1.15C3.166 6.665 2.004 8.415 2.004 11v8a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1h-2V5ZM19.004 5a1 1 0 0 0-.43-.822c-.57-.395-1.176-.031-1.685.255-.428.24-.998.614-1.569 1.15-1.154 1.082-2.316 2.832-2.316 5.417v8a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1h-2V5Z"/></svg>

After

Width:  |  Height:  |  Size: 455 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M7.574 4.178a1 1 0 0 1 .43.822v5h2a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-8c0-2.585 1.162-4.335 2.316-5.417a8.163 8.163 0 0 1 1.569-1.15 7.029 7.029 0 0 1 .738-.36l.016-.005.005-.003h.003v-.001c.001 0 .002 0 .353.936l-.351-.936a1 1 0 0 1 .92.114Zm-1.57 2.588a5.99 5.99 0 0 0-.316.276C4.842 7.835 4.004 9.085 4.004 11v7h5v-6h-2a1 1 0 0 1-1-1V6.766Zm12.57-2.588a1 1 0 0 1 .43.822v5h2a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-8c0-2.585 1.162-4.335 2.316-5.417a8.166 8.166 0 0 1 1.569-1.15 7.038 7.038 0 0 1 .738-.36l.016-.005.005-.003h.003v-.001c.001 0 .002 0 .353.936l-.351-.936a1 1 0 0 1 .92.114Zm-1.57 2.588c-.105.085-.21.177-.316.276-.846.793-1.684 2.043-1.684 3.958v7h5v-6h-2a1 1 0 0 1-1-1V6.766Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 857 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M16.293 2.293a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1 0 1.414l-3 3a1 1 0 0 1-1.414-1.414L17.586 7H5v4a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1h13.586l-1.293-1.293a1 1 0 0 1 0-1.414ZM21 13v5a1 1 0 0 1-1 1H6.414l1.293 1.293a1 1 0 1 1-1.414 1.414l-3-3a1 1 0 0 1 0-1.414l3-3a1 1 0 0 1 1.414 1.414L6.414 17H19v-4a1 1 0 1 1 2 0Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 446 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M16.793 2.293a1 1 0 0 1 1.414 0L20.5 4.586a2 2 0 0 1 0 2.828l-2.293 2.293a1 1 0 0 1-1.414-1.414L18.086 7H7a2 2 0 0 0-2 2v2a1 1 0 1 1-2 0V9a4 4 0 0 1 4-4h11.086l-1.293-1.293a1 1 0 0 1 0-1.414ZM20 12a1 1 0 0 1 1 1v2a4 4 0 0 1-4 4H5.914l1.293 1.293a1 1 0 1 1-1.414 1.414L3.5 19.414a2 2 0 0 1 0-2.828l2.293-2.293a1 1 0 0 1 1.414 1.414L5.914 17H17a2 2 0 0 0 2-2v-2a1 1 0 0 1 1-1Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 517 B

View File

@ -841,7 +841,7 @@ export const atoms = {
marginRight: 'auto', marginRight: 'auto',
}, },
/* /*
* Pointer events * Pointer events & user select
*/ */
pointer_events_none: { pointer_events_none: {
pointerEvents: 'none', pointerEvents: 'none',
@ -849,6 +849,15 @@ export const atoms = {
pointer_events_auto: { pointer_events_auto: {
pointerEvents: 'auto', pointerEvents: 'auto',
}, },
user_select_none: {
userSelect: 'none',
},
user_select_text: {
userSelect: 'text',
},
user_select_all: {
userSelect: 'all',
},
/* /*
* Text decoration * Text decoration
*/ */

View File

@ -71,6 +71,7 @@ export type ButtonProps = Pick<
testID?: string testID?: string
label: string label: string
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
hoverStyle?: StyleProp<ViewStyle>
children: NonTextElements | ((context: ButtonContext) => NonTextElements) children: NonTextElements | ((context: ButtonContext) => NonTextElements)
} }
@ -96,6 +97,7 @@ export function Button({
label, label,
disabled = false, disabled = false,
style, style,
hoverStyle: hoverStyleProp,
...rest ...rest
}: ButtonProps) { }: ButtonProps) {
const t = useTheme() const t = useTheme()
@ -374,7 +376,9 @@ export function Button({
a.align_center, a.align_center,
a.justify_center, a.justify_center,
flattenedBaseStyles, flattenedBaseStyles,
...(state.hovered || state.pressed ? hoverStyles : []), ...(state.hovered || state.pressed
? [hoverStyles, flatten(hoverStyleProp)]
: []),
flatten(style), flatten(style),
]} ]}
onPressIn={onPressIn} onPressIn={onPressIn}

View File

@ -0,0 +1,21 @@
import {createSinglePathSVG} from './TEMPLATE'
export const OpenQuote_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M7.574 4.178a1 1 0 0 1 .43.822v5h2a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-8c0-2.585 1.162-4.335 2.316-5.417a8.163 8.163 0 0 1 1.569-1.15 7.029 7.029 0 0 1 .738-.36l.016-.005.005-.003h.003v-.001c.001 0 .002 0 .353.936l-.351-.936a1 1 0 0 1 .92.114Zm-1.57 2.588a5.99 5.99 0 0 0-.316.276C4.842 7.835 4.004 9.085 4.004 11v7h5v-6h-2a1 1 0 0 1-1-1V6.766Zm12.57-2.588a1 1 0 0 1 .43.822v5h2a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-8c0-2.585 1.162-4.335 2.316-5.417a8.166 8.166 0 0 1 1.569-1.15 7.038 7.038 0 0 1 .738-.36l.016-.005.005-.003h.003v-.001c.001 0 .002 0 .353.936l-.351-.936a1 1 0 0 1 .92.114Zm-1.57 2.588c-.105.085-.21.177-.316.276-.846.793-1.684 2.043-1.684 3.958v7h5v-6h-2a1 1 0 0 1-1-1V6.766Z',
})
export const OpenQuote_Filled_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M8.004 5a1 1 0 0 0-.43-.822c-.57-.395-1.176-.031-1.685.255-.428.24-.998.614-1.569 1.15C3.166 6.665 2.004 8.415 2.004 11v8a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1h-2V5ZM19.004 5a1 1 0 0 0-.43-.822c-.57-.395-1.176-.031-1.685.255-.428.24-.998.614-1.569 1.15-1.154 1.082-2.316 2.832-2.316 5.417v8a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1h-2V5Z',
})
export const CloseQuote_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M2.004 5a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v8c0 2.585-1.162 4.335-2.316 5.417-.571.536-1.14.91-1.569 1.15a7.01 7.01 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001L6.004 19l.351.936A1 1 0 0 1 5.004 19v-5h-2a1 1 0 0 1-1-1V5Zm5 12.234c.104-.085.21-.177.316-.276.846-.793 1.684-2.043 1.684-3.958V6h-5v6h2a1 1 0 0 1 1 1v4.234Zm6-12.234a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v8c0 2.585-1.162 4.335-2.316 5.417-.571.536-1.14.91-1.569 1.15a7.018 7.018 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001-.352-.936.351.936A1 1 0 0 1 16.004 19v-5h-2a1 1 0 0 1-1-1V5Zm5 12.234V13a1 1 0 0 0-1-1h-2V6h5v7c0 1.915-.838 3.165-1.684 3.958-.106.1-.212.191-.316.276Z',
})
export const CloseQuote_Stroke2_Corner1_Rounded = createSinglePathSVG({
path: 'M2.003 5.999a2 2 0 0 1 2-1.999h5c1.104 0 2 .893 2 1.999V13c0 2.585-1.16 4.335-2.315 5.417-.571.536-1.14.91-1.569 1.15a7.01 7.01 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001L6.004 19l.351.936a1 1 0 0 1-1.351-.935L5 14H4a2 2 0 0 1-2-2.001l.002-6Zm5 11.236L7 12.999A1 1 0 0 0 6 12H4l.003-6h5v7c0 1.915-.837 3.165-1.683 3.958-.106.1-.213.192-.317.277Zm6-11.235a2 2 0 0 1 2-2h5c1.104 0 2 .893 2 1.999V13c0 2.585-1.16 4.335-2.315 5.417-.571.536-1.14.91-1.569 1.15a7.018 7.018 0 0 1-.738.36l-.016.006-.006.002h-.002l-.001.001-.352-.936.351.936A1 1 0 0 1 16.004 19v-5h-1a2 2 0 0 1-2-2V6Zm7 0h-5v6h2a1 1 0 0 1 1 1v4.234c.105-.085.211-.177.317-.276.846-.793 1.684-2.043 1.684-3.958V6Z',
})
export const CloseQuote_Filled_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M3.004 4a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2v5a1 1 0 0 0 .43.822c.57.395 1.176.031 1.685-.255.428-.24.998-.614 1.569-1.15 1.154-1.082 2.316-2.832 2.316-5.417V5a1 1 0 0 0-1-1h-7ZM14.004 4a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2v5a1 1 0 0 0 .43.822c.57.395 1.176.031 1.685-.255.428-.24.998-.614 1.569-1.15 1.154-1.082 2.316-2.832 2.316-5.417V5a1 1 0 0 0-1-1h-7Z',
})

View File

@ -0,0 +1,13 @@
import {createSinglePathSVG} from './TEMPLATE'
export const Repost_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M16.293 2.293a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1 0 1.414l-3 3a1 1 0 0 1-1.414-1.414L17.586 7H5v4a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1h13.586l-1.293-1.293a1 1 0 0 1 0-1.414ZM21 13v5a1 1 0 0 1-1 1H6.414l1.293 1.293a1 1 0 1 1-1.414 1.414l-3-3a1 1 0 0 1 0-1.414l3-3a1 1 0 0 1 1.414 1.414L6.414 17H19v-4a1 1 0 1 1 2 0Z',
})
export const Repost_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M17.957 2.293a1 1 0 1 0-1.414 1.414L17.836 5H6a3 3 0 0 0-3 3v3a1 1 0 1 0 2 0V8a1 1 0 0 1 1-1h11.836l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.47-2.47a1.75 1.75 0 0 0 0-2.474l-2.47-2.47ZM20 12a1 1 0 0 1 1 1v3a3 3 0 0 1-3 3H6.164l1.293 1.293a1 1 0 1 1-1.414 1.414l-2.47-2.47a1.75 1.75 0 0 1 0-2.474l2.47-2.47a1 1 0 0 1 1.414 1.414L6.164 17H18a1 1 0 0 0 1-1v-3a1 1 0 0 1 1-1Z',
})
export const Repost_Stroke2_Corner3_Rounded = createSinglePathSVG({
path: 'M16.793 2.293a1 1 0 0 1 1.414 0L20.5 4.586a2 2 0 0 1 0 2.828l-2.293 2.293a1 1 0 0 1-1.414-1.414L18.086 7H7a2 2 0 0 0-2 2v2a1 1 0 1 1-2 0V9a4 4 0 0 1 4-4h11.086l-1.293-1.293a1 1 0 0 1 0-1.414ZM20 12a1 1 0 0 1 1 1v2a4 4 0 0 1-4 4H5.914l1.293 1.293a1 1 0 1 1-1.414 1.414L3.5 19.414a2 2 0 0 1 0-2.828l2.293-2.293a1 1 0 0 1 1.414 1.414L5.914 17H17a2 2 0 0 0 2-2v-2a1 1 0 0 1 1-1Z',
})

View File

@ -367,7 +367,7 @@ let PostThreadItemLoaded = ({
) : null} ) : null}
</View> </View>
) : null} ) : null}
<View style={[s.pl10, s.pr10, s.pb5]}> <View style={[s.pl10, s.pr10]}>
<PostCtrls <PostCtrls
big big
post={post} post={post}
@ -733,7 +733,7 @@ const styles = StyleSheet.create({
borderTopWidth: 1, borderTopWidth: 1,
borderBottomWidth: 1, borderBottomWidth: 1,
marginTop: 5, marginTop: 5,
marginBottom: 15, marginBottom: 10,
}, },
expandedInfoItem: { expandedInfoItem: {
marginRight: 10, marginRight: 10,

View File

@ -1,20 +1,20 @@
import React from 'react' import React from 'react'
import { import {
StyleSheet, DimensionValue,
StyleProp, StyleProp,
StyleSheet,
View, View,
ViewStyle, ViewStyle,
DimensionValue,
} from 'react-native' } from 'react-native'
import {
HeartIcon, import {usePalette} from 'lib/hooks/usePalette'
HeartIconSolid, import {HeartIconSolid} from 'lib/icons'
CommentBottomArrow,
RepostIcon,
} from 'lib/icons'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {useTheme} from 'lib/ThemeContext' import {useTheme} from 'lib/ThemeContext'
import {usePalette} from 'lib/hooks/usePalette' import {useTheme as useTheme_NEW} from '#/alf'
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
import {Heart2_Stroke2_Corner0_Rounded as HeartIconOutline} from '#/components/icons/Heart2'
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
export function LoadingPlaceholder({ export function LoadingPlaceholder({
width, width,
@ -46,7 +46,7 @@ export function PostLoadingPlaceholder({
}: { }: {
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
}) { }) {
const theme = useTheme() const t = useTheme_NEW()
const pal = usePalette('default') const pal = usePalette('default')
return ( return (
<View style={[styles.post, pal.view, style]}> <View style={[styles.post, pal.view, style]}>
@ -67,35 +67,47 @@ export function PostLoadingPlaceholder({
<LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} />
<LoadingPlaceholder width="80%" height={6} style={{marginBottom: 11}} /> <LoadingPlaceholder width="80%" height={6} style={{marginBottom: 11}} />
<View style={styles.postCtrls}> <View style={styles.postCtrls}>
<View style={styles.postCtrl}> <View style={[styles.postCtrl, {marginLeft: -5}]}>
<View style={[styles.postBtn, {paddingLeft: 0}]}> <View style={styles.postBtn}>
<CommentBottomArrow <Bubble
style={[{color: theme.palette.default.icon, marginTop: 1}]} style={[
strokeWidth={3} {
size={15} color: t.palette.contrast_500,
},
{pointerEvents: 'none'},
]}
width={18}
/> />
</View> </View>
</View> </View>
<View style={styles.postCtrl}> <View style={styles.postCtrl}>
<View style={styles.postBtn}> <View style={styles.postBtn}>
<RepostIcon <Repost
style={{color: theme.palette.default.icon}} style={[
strokeWidth={3} {
size={20} color: t.palette.contrast_500,
},
{pointerEvents: 'none'},
]}
width={18}
/> />
</View> </View>
</View> </View>
<View style={styles.postCtrl}> <View style={styles.postCtrl}>
<View style={styles.postBtn}> <View style={styles.postBtn}>
<HeartIcon <HeartIconOutline
style={{color: theme.palette.default.icon} as ViewStyle} style={[
size={16} {
strokeWidth={3} color: t.palette.contrast_500,
},
{pointerEvents: 'none'},
]}
width={18}
/> />
</View> </View>
</View> </View>
<View style={styles.postCtrl}> <View style={styles.postCtrl}>
<View style={styles.postBtn} /> <View style={[styles.postBtn, {minHeight: 30}]} />
</View> </View>
</View> </View>
</View> </View>
@ -290,10 +302,10 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
}, },
postBtn: { postBtn: {
padding: 5,
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
padding: 5,
}, },
avatar: { avatar: {
borderRadius: 26, borderRadius: 26,

View File

@ -1,5 +1,10 @@
import React, {memo} from 'react' import React, {memo} from 'react'
import {Pressable, PressableProps, StyleProp, ViewStyle} from 'react-native' import {
Pressable,
type PressableProps,
type StyleProp,
type ViewStyle,
} from 'react-native'
import * as Clipboard from 'expo-clipboard' import * as Clipboard from 'expo-clipboard'
import { import {
AppBskyActorDefs, AppBskyActorDefs,
@ -7,7 +12,6 @@ import {
AtUri, AtUri,
RichText as RichTextAPI, RichText as RichTextAPI,
} from '@atproto/api' } from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
@ -37,6 +41,7 @@ import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons
import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble' import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBrackets} from '#/components/icons/CodeBrackets' import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBrackets} from '#/components/icons/CodeBrackets'
import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import { import {
EmojiSad_Stroke2_Corner0_Rounded as EmojiSad, EmojiSad_Stroke2_Corner0_Rounded as EmojiSad,
EmojiSmile_Stroke2_Corner0_Rounded as EmojiSmile, EmojiSmile_Stroke2_Corner0_Rounded as EmojiSmile,
@ -68,6 +73,7 @@ let PostDropdownBtn = ({
richText, richText,
style, style,
hitSlop, hitSlop,
size,
timestamp, timestamp,
}: { }: {
testID: string testID: string
@ -79,6 +85,7 @@ let PostDropdownBtn = ({
richText: RichTextAPI richText: RichTextAPI
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
hitSlop?: PressableProps['hitSlop'] hitSlop?: PressableProps['hitSlop']
size?: 'lg' | 'md' | 'sm'
timestamp: string timestamp: string
}): React.ReactNode => { }): React.ReactNode => {
const {hasSession, currentAccount} = useSession() const {hasSession, currentAccount} = useSession()
@ -238,14 +245,13 @@ let PostDropdownBtn = ({
style, style,
a.rounded_full, a.rounded_full,
(state.hovered || state.pressed) && [ (state.hovered || state.pressed) && [
alf.atoms.bg_contrast_50, alf.atoms.bg_contrast_25,
], ],
]}> ]}>
<FontAwesomeIcon <DotsHorizontal
icon="ellipsis" fill={defaultCtrlColor}
size={20}
color={defaultCtrlColor}
style={{pointerEvents: 'none'}} style={{pointerEvents: 'none'}}
size={size}
/> />
</Pressable> </Pressable>
) )

View File

@ -1,10 +1,10 @@
import React, {memo, useCallback} from 'react' import React, {memo, useCallback} from 'react'
import { import {
StyleProp, Pressable,
StyleSheet, type PressableStateCallbackType,
TouchableOpacity, type StyleProp,
View, View,
ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import { import {
AppBskyFeedDefs, AppBskyFeedDefs,
@ -16,12 +16,11 @@ import {msg, plural} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {HITSLOP_10, HITSLOP_20} from '#/lib/constants' import {HITSLOP_10, HITSLOP_20} from '#/lib/constants'
import {CommentBottomArrow, HeartIcon, HeartIconSolid} from '#/lib/icons' import {useHaptics} from '#/lib/haptics'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers' import {toShareUrl} from '#/lib/strings/url-helpers'
import {s} from '#/lib/styles' import {s} from '#/lib/styles'
import {useTheme} from '#/lib/ThemeContext'
import {Shadow} from '#/state/cache/types' import {Shadow} from '#/state/cache/types'
import {useFeedFeedbackContext} from '#/state/feed-feedback' import {useFeedFeedbackContext} from '#/state/feed-feedback'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
@ -31,9 +30,14 @@ import {
} from '#/state/queries/post' } from '#/state/queries/post'
import {useRequireAuth} from '#/state/session' import {useRequireAuth} from '#/state/session'
import {useComposerControls} from '#/state/shell/composer' import {useComposerControls} from '#/state/shell/composer'
import {useHaptics} from 'lib/haptics' import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog' import {useDialogControl} from '#/components/Dialog'
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox'
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
import {
Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled,
Heart2_Stroke2_Corner0_Rounded as HeartIconOutline,
} from '#/components/icons/Heart2'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {PostDropdownBtn} from '../forms/PostDropdownBtn' import {PostDropdownBtn} from '../forms/PostDropdownBtn'
import {Text} from '../text/Text' import {Text} from '../text/Text'
@ -58,7 +62,7 @@ let PostCtrls = ({
onPressReply: () => void onPressReply: () => void
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
}): React.ReactNode => { }): React.ReactNode => {
const theme = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const {openComposer} = useComposerControls() const {openComposer} = useComposerControls()
const {closeModal} = useModalControls() const {closeModal} = useModalControls()
@ -80,9 +84,9 @@ let PostCtrls = ({
const defaultCtrlColor = React.useMemo( const defaultCtrlColor = React.useMemo(
() => ({ () => ({
color: theme.palette.default.postCtrl, color: t.palette.contrast_500,
}), }),
[theme], [t],
) as StyleProp<ViewStyle> ) as StyleProp<ViewStyle>
const onPressToggleLike = React.useCallback(async () => { const onPressToggleLike = React.useCallback(async () => {
@ -185,57 +189,70 @@ let PostCtrls = ({
}) })
}, [post.uri, post.author, sendInteraction, feedContext]) }, [post.uri, post.author, sendInteraction, feedContext])
const btnStyle = React.useCallback(
({pressed, hovered}: PressableStateCallbackType) => [
a.gap_xs,
a.rounded_full,
a.flex_row,
a.align_center,
a.justify_center,
{padding: 5},
(pressed || hovered) && t.atoms.bg_contrast_25,
],
[t.atoms.bg_contrast_25],
)
return ( return (
<View style={[styles.ctrls, style]}> <View style={[a.flex_row, a.justify_between, a.align_center, style]}>
<View <View
style={[ style={[
big ? styles.ctrlBig : styles.ctrl, big ? a.align_center : [a.flex_1, a.align_start, {marginLeft: -5}],
post.viewer?.replyDisabled ? {opacity: 0.5} : undefined, post.viewer?.replyDisabled ? {opacity: 0.5} : undefined,
]}> ]}>
<TouchableOpacity <Pressable
testID="replyBtn" testID="replyBtn"
style={[styles.btn, !big && styles.btnPad, {paddingLeft: 0}]} style={btnStyle}
onPress={() => { onPress={() => {
if (!post.viewer?.replyDisabled) { if (!post.viewer?.replyDisabled) {
requireAuth(() => onPressReply()) requireAuth(() => onPressReply())
} }
}} }}
accessibilityRole="button"
accessibilityLabel={plural(post.replyCount || 0, { accessibilityLabel={plural(post.replyCount || 0, {
one: 'Reply (# reply)', one: 'Reply (# reply)',
other: 'Reply (# replies)', other: 'Reply (# replies)',
})} })}
accessibilityHint="" accessibilityHint=""
hitSlop={big ? HITSLOP_20 : HITSLOP_10}> hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
<CommentBottomArrow <Bubble
style={[defaultCtrlColor, big ? s.mt2 : styles.mt1]} style={[defaultCtrlColor, {pointerEvents: 'none'}]}
strokeWidth={3} width={big ? 22 : 18}
size={big ? 20 : 15}
/> />
{typeof post.replyCount !== 'undefined' && post.replyCount > 0 ? ( {typeof post.replyCount !== 'undefined' && post.replyCount > 0 ? (
<Text style={[defaultCtrlColor, s.ml5, s.f15]}> <Text
style={[
defaultCtrlColor,
big ? a.text_md : {fontSize: 15},
a.user_select_none,
]}>
{post.replyCount} {post.replyCount}
</Text> </Text>
) : undefined} ) : undefined}
</TouchableOpacity> </Pressable>
</View> </View>
<View style={big ? styles.ctrlBig : styles.ctrl}> <View style={big ? a.align_center : [a.flex_1, a.align_start]}>
<RepostButton <RepostButton
big={big}
isReposted={!!post.viewer?.repost} isReposted={!!post.viewer?.repost}
repostCount={post.repostCount} repostCount={post.repostCount}
onRepost={onRepost} onRepost={onRepost}
onQuote={onQuote} onQuote={onQuote}
big={big}
/> />
</View> </View>
<View style={big ? styles.ctrlBig : styles.ctrl}> <View style={big ? a.align_center : [a.flex_1, a.align_start]}>
<TouchableOpacity <Pressable
testID="likeBtn" testID="likeBtn"
style={[styles.btn, !big && styles.btnPad]} style={btnStyle}
onPress={() => { onPress={() => requireAuth(() => onPressToggleLike())}
requireAuth(() => onPressToggleLike())
}}
accessibilityRole="button"
accessibilityLabel={ accessibilityLabel={
post.viewer?.like post.viewer?.like
? plural(post.likeCount || 0, { ? plural(post.likeCount || 0, {
@ -250,33 +267,36 @@ let PostCtrls = ({
accessibilityHint="" accessibilityHint=""
hitSlop={big ? HITSLOP_20 : HITSLOP_10}> hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
{post.viewer?.like ? ( {post.viewer?.like ? (
<HeartIconSolid style={s.likeColor} size={big ? 22 : 16} /> <HeartIconFilled style={s.likeColor} width={big ? 22 : 18} />
) : ( ) : (
<HeartIcon <HeartIconOutline
style={[defaultCtrlColor, big ? styles.mt1 : undefined]} style={[defaultCtrlColor, {pointerEvents: 'none'}]}
strokeWidth={3} width={big ? 22 : 18}
size={big ? 20 : 16}
/> />
)} )}
{typeof post.likeCount !== 'undefined' && post.likeCount > 0 ? ( {typeof post.likeCount !== 'undefined' && post.likeCount > 0 ? (
<Text <Text
testID="likeCount" testID="likeCount"
style={ style={[
post.viewer?.like [
? [s.bold, s.likeColor, s.f15, s.ml5] big ? a.text_md : {fontSize: 15},
: [defaultCtrlColor, s.f15, s.ml5] a.user_select_none,
}> post.viewer?.like
? [a.font_bold, s.likeColor]
: defaultCtrlColor,
],
]}>
{post.likeCount} {post.likeCount}
</Text> </Text>
) : undefined} ) : undefined}
</TouchableOpacity> </Pressable>
</View> </View>
{big && ( {big && (
<> <>
<View style={styles.ctrlBig}> <View style={a.align_center}>
<TouchableOpacity <Pressable
testID="shareBtn" testID="shareBtn"
style={[styles.btn]} style={btnStyle}
onPress={() => { onPress={() => {
if (shouldShowLoggedOutWarning) { if (shouldShowLoggedOutWarning) {
loggedOutWarningPromptControl.open() loggedOutWarningPromptControl.open()
@ -284,15 +304,14 @@ let PostCtrls = ({
onShare() onShare()
} }
}} }}
accessibilityRole="button" accessibilityLabel={_(msg`Share`)}
accessibilityLabel={`${_(msg`Share`)}`}
accessibilityHint="" accessibilityHint=""
hitSlop={big ? HITSLOP_20 : HITSLOP_10}> hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
<ArrowOutOfBox <ArrowOutOfBox
style={[defaultCtrlColor, styles.mt1]} style={[defaultCtrlColor, {pointerEvents: 'none'}]}
width={22} width={22}
/> />
</TouchableOpacity> </Pressable>
</View> </View>
<Prompt.Basic <Prompt.Basic
control={loggedOutWarningPromptControl} control={loggedOutWarningPromptControl}
@ -305,7 +324,7 @@ let PostCtrls = ({
/> />
</> </>
)} )}
<View style={big ? styles.ctrlBig : styles.ctrl}> <View style={big ? a.align_center : [a.flex_1, a.align_start]}>
<PostDropdownBtn <PostDropdownBtn
testID="postDropdownBtn" testID="postDropdownBtn"
postAuthor={post.author} postAuthor={post.author}
@ -314,7 +333,7 @@ let PostCtrls = ({
postFeedContext={feedContext} postFeedContext={feedContext}
record={record} record={record}
richText={richText} richText={richText}
style={styles.btnPad} style={{padding: 5}}
hitSlop={big ? HITSLOP_20 : HITSLOP_10} hitSlop={big ? HITSLOP_20 : HITSLOP_10}
timestamp={post.indexedAt} timestamp={post.indexedAt}
/> />
@ -324,31 +343,3 @@ let PostCtrls = ({
} }
PostCtrls = memo(PostCtrls) PostCtrls = memo(PostCtrls)
export {PostCtrls} export {PostCtrls}
const styles = StyleSheet.create({
ctrls: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
ctrl: {
flex: 1,
alignItems: 'flex-start',
},
ctrlBig: {
alignItems: 'center',
},
btn: {
flexDirection: 'row',
alignItems: 'center',
},
btnPad: {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: 5,
paddingRight: 5,
},
mt1: {
marginTop: 1,
},
})

View File

@ -1,108 +1,132 @@
import React, {memo, useCallback} from 'react' import React, {memo, useCallback} from 'react'
import {StyleProp, StyleSheet, TouchableOpacity, ViewStyle} from 'react-native' import {View} from 'react-native'
import {msg, plural} from '@lingui/macro' import {msg, plural} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useModalControls} from '#/state/modals'
import {useRequireAuth} from '#/state/session' import {useRequireAuth} from '#/state/session'
import {HITSLOP_10, HITSLOP_20} from 'lib/constants' import {atoms as a, useTheme} from '#/alf'
import {RepostIcon} from 'lib/icons' import {Button, ButtonText} from '#/components/Button'
import {colors, s} from 'lib/styles' import * as Dialog from '#/components/Dialog'
import {useTheme} from 'lib/ThemeContext' import {CloseQuote_Stroke2_Corner1_Rounded as Quote} from '#/components/icons/Quote'
import {Text} from '../text/Text' import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
import {Text} from '#/components/Typography'
interface Props { interface Props {
isReposted: boolean isReposted: boolean
repostCount?: number repostCount?: number
big?: boolean
onRepost: () => void onRepost: () => void
onQuote: () => void onQuote: () => void
big?: boolean
} }
let RepostButton = ({ let RepostButton = ({
isReposted, isReposted,
repostCount, repostCount,
big,
onRepost, onRepost,
onQuote, onQuote,
big,
}: Props): React.ReactNode => { }: Props): React.ReactNode => {
const theme = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const {openModal} = useModalControls()
const requireAuth = useRequireAuth() const requireAuth = useRequireAuth()
const dialogControl = Dialog.useDialogControl()
const defaultControlColor = React.useMemo( const color = React.useMemo(
() => ({ () => ({
color: theme.palette.default.postCtrl, color: isReposted ? t.palette.positive_600 : t.palette.contrast_500,
}), }),
[theme], [t, isReposted],
) )
const onPressToggleRepostWrapper = useCallback(() => { const close = useCallback(() => dialogControl.close(), [dialogControl])
openModal({
name: 'repost',
onRepost: onRepost,
onQuote: onQuote,
isReposted,
})
}, [onRepost, onQuote, isReposted, openModal])
return ( return (
<TouchableOpacity <>
testID="repostBtn" <Button
onPress={() => { testID="repostBtn"
requireAuth(() => onPressToggleRepostWrapper()) onPress={() => {
}} requireAuth(() => dialogControl.open())
style={[styles.btn, !big && styles.btnPad]} }}
accessibilityRole="button" style={[a.flex_row, a.align_center, a.gap_xs, {padding: 5}]}
accessibilityLabel={`${ hoverStyle={t.atoms.bg_contrast_25}
isReposted label={`${
? _(msg`Undo repost`)
: _(msg({message: 'Repost', context: 'action'}))
} (${plural(repostCount || 0, {one: '# repost', other: '# reposts'})})`}
accessibilityHint=""
hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
<RepostIcon
style={
isReposted isReposted
? (styles.reposted as StyleProp<ViewStyle>) ? _(msg`Undo repost`)
: defaultControlColor : _(msg({message: 'Repost', context: 'action'}))
} } (${plural(repostCount || 0, {one: '# repost', other: '# reposts'})})`}
strokeWidth={2.4} shape="round"
size={big ? 24 : 20} variant="ghost"
/> color="secondary">
{typeof repostCount !== 'undefined' && repostCount > 0 ? ( <Repost style={color} width={big ? 22 : 18} />
<Text {typeof repostCount !== 'undefined' && repostCount > 0 ? (
testID="repostCount" <Text
style={ testID="repostCount"
isReposted style={[
? [s.bold, s.green3, s.f15, s.ml5] color,
: [defaultControlColor, s.f15, s.ml5] big ? a.text_md : {fontSize: 15},
}> isReposted && a.font_bold,
{repostCount} ]}>
</Text> {repostCount}
) : undefined} </Text>
</TouchableOpacity> ) : undefined}
</Button>
<Dialog.Outer control={dialogControl}>
<Dialog.Handle />
<Dialog.Inner label={_(msg`Repost or quote post`)}>
<View style={a.gap_xl}>
<View style={a.gap_xs}>
<Button
style={[a.justify_start, a.px_md]}
label={
isReposted
? _(msg`Remove repost`)
: _(msg({message: `Repost`, context: 'action'}))
}
onPress={() => {
dialogControl.close()
onRepost()
}}
size="large"
variant="ghost"
color="primary">
<Repost size="lg" fill={t.palette.primary_500} />
<Text style={[a.font_bold, a.text_xl]}>
{isReposted
? _(msg`Remove repost`)
: _(msg({message: `Repost`, context: 'action'}))}
</Text>
</Button>
<Button
style={[a.justify_start, a.px_md]}
label={_(msg`Quote post`)}
onPress={() => {
dialogControl.close(() => {
onQuote()
})
}}
size="large"
variant="ghost"
color="primary">
<Quote size="lg" fill={t.palette.primary_500} />
<Text style={[a.font_bold, a.text_xl]}>
{_(msg`Quote post`)}
</Text>
</Button>
</View>
<Button
label={_(msg`Cancel quote post`)}
onAccessibilityEscape={close}
onPress={close}
size="medium"
variant="solid"
color="primary">
<ButtonText>{_(msg`Cancel`)}</ButtonText>
</Button>
</View>
</Dialog.Inner>
</Dialog.Outer>
</>
) )
} }
RepostButton = memo(RepostButton) RepostButton = memo(RepostButton)
export {RepostButton} export {RepostButton}
const styles = StyleSheet.create({
btn: {
flexDirection: 'row',
alignItems: 'center',
},
btnPad: {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: 5,
paddingRight: 5,
},
reposted: {
color: colors.green3,
},
repostCount: {
color: 'currentColor',
},
})

View File

@ -1,130 +1,134 @@
import React from 'react' import React from 'react'
import {StyleProp, StyleSheet, View, ViewStyle, Pressable} from 'react-native' import {Pressable, View} from 'react-native'
import {RepostIcon} from 'lib/icons'
import {colors} from 'lib/styles'
import {useTheme} from 'lib/ThemeContext'
import {Text} from '../text/Text'
import {
NativeDropdown,
DropdownItem as NativeDropdownItem,
} from '../forms/NativeDropdown'
import {EventStopper} from '../EventStopper'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useRequireAuth} from '#/state/session' import {useRequireAuth} from '#/state/session'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {CloseQuote_Stroke2_Corner1_Rounded as Quote} from '#/components/icons/Quote'
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
import * as Menu from '#/components/Menu'
import {Text} from '#/components/Typography'
import {EventStopper} from '../EventStopper'
interface Props { interface Props {
isReposted: boolean isReposted: boolean
repostCount?: number repostCount?: number
big?: boolean
onRepost: () => void onRepost: () => void
onQuote: () => void onQuote: () => void
style?: StyleProp<ViewStyle> big?: boolean
} }
export const RepostButton = ({ export const RepostButton = ({
isReposted, isReposted,
repostCount, repostCount,
big,
onRepost, onRepost,
onQuote, onQuote,
big,
}: Props) => { }: Props) => {
const theme = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const {hasSession} = useSession() const {hasSession} = useSession()
const requireAuth = useRequireAuth() const requireAuth = useRequireAuth()
const defaultControlColor = React.useMemo( const color = React.useMemo(
() => ({ () => ({
color: theme.palette.default.postCtrl, color: isReposted ? t.palette.positive_600 : t.palette.contrast_500,
}), }),
[theme], [t, isReposted],
)
const dropdownItems: NativeDropdownItem[] = [
{
label: isReposted ? _(msg`Undo repost`) : _(msg`Repost`),
testID: 'repostDropdownRepostBtn',
icon: {
ios: {name: 'repeat'},
android: '',
web: 'retweet',
},
onPress: onRepost,
},
{
label: _(msg`Quote post`),
testID: 'repostDropdownQuoteBtn',
icon: {
ios: {name: 'quote.bubble'},
android: '',
web: 'quote-left',
},
onPress: onQuote,
},
]
const inner = (
<View
style={[
styles.btn,
!big && styles.btnPad,
(isReposted
? styles.reposted
: defaultControlColor) as StyleProp<ViewStyle>,
]}>
<RepostIcon strokeWidth={2.2} size={big ? 24 : 20} />
{typeof repostCount !== 'undefined' && repostCount > 0 ? (
<Text
testID="repostCount"
type={isReposted ? 'md-bold' : 'md'}
style={styles.repostCount}>
{repostCount}
</Text>
) : undefined}
</View>
) )
return hasSession ? ( return hasSession ? (
<EventStopper> <EventStopper onKeyDown={false}>
<NativeDropdown <Menu.Root>
items={dropdownItems} <Menu.Trigger label={_(msg`Repost or quote post`)}>
accessibilityLabel={_(msg`Repost or quote post`)} {({props, state}) => {
accessibilityHint=""> return (
{inner} <Pressable
</NativeDropdown> {...props}
style={[
a.rounded_full,
(state.hovered || state.pressed) && {
backgroundColor: t.palette.contrast_25,
},
]}>
<RepostInner
isReposted={isReposted}
color={color}
repostCount={repostCount}
big={big}
/>
</Pressable>
)
}}
</Menu.Trigger>
<Menu.Outer style={{minWidth: 170}}>
<Menu.Item
label={isReposted ? _(msg`Undo repost`) : _(msg`Repost`)}
testID="repostDropdownRepostBtn"
onPress={onRepost}>
<Menu.ItemText>
{isReposted ? _(msg`Undo repost`) : _(msg`Repost`)}
</Menu.ItemText>
<Menu.ItemIcon icon={Repost} position="right" />
</Menu.Item>
<Menu.Item
label={_(msg`Quote post`)}
testID="repostDropdownQuoteBtn"
onPress={onQuote}>
<Menu.ItemText>{_(msg`Quote post`)}</Menu.ItemText>
<Menu.ItemIcon icon={Quote} position="right" />
</Menu.Item>
</Menu.Outer>
</Menu.Root>
</EventStopper> </EventStopper>
) : ( ) : (
<Pressable <Button
accessibilityRole="button"
onPress={() => { onPress={() => {
requireAuth(() => {}) requireAuth(() => {})
}} }}
accessibilityLabel={_(msg`Repost or quote post`)} label={_(msg`Repost or quote post`)}
accessibilityHint=""> style={{padding: 0}}
{inner} hoverStyle={t.atoms.bg_contrast_25}
</Pressable> shape="round"
variant="ghost"
color="secondary">
<RepostInner
isReposted={isReposted}
color={color}
repostCount={repostCount}
big={big}
/>
</Button>
) )
} }
const styles = StyleSheet.create({ const RepostInner = ({
btn: { isReposted,
flexDirection: 'row', color,
alignItems: 'center', repostCount,
gap: 4, big,
}, }: {
btnPad: { isReposted: boolean
paddingTop: 5, color: {color: string}
paddingBottom: 5, repostCount?: number
paddingLeft: 5, big?: boolean
paddingRight: 5, }) => (
}, <View style={[a.flex_row, a.align_center, a.gap_xs, {padding: 5}]}>
reposted: { <Repost style={color} width={big ? 22 : 18} />
color: colors.green3, {typeof repostCount !== 'undefined' && repostCount > 0 ? (
}, <Text
repostCount: { testID="repostCount"
color: 'currentColor', style={[
}, color,
}) big ? a.text_md : {fontSize: 15},
isReposted && [a.font_bold],
a.user_select_none,
]}>
{repostCount}
</Text>
) : undefined}
</View>
)