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
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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',
|
||||||
|
})
|
|
@ -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',
|
||||||
|
})
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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={[
|
||||||
|
[
|
||||||
|
big ? a.text_md : {fontSize: 15},
|
||||||
|
a.user_select_none,
|
||||||
post.viewer?.like
|
post.viewer?.like
|
||||||
? [s.bold, s.likeColor, s.f15, s.ml5]
|
? [a.font_bold, s.likeColor]
|
||||||
: [defaultCtrlColor, s.f15, s.ml5]
|
: 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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
<>
|
||||||
|
<Button
|
||||||
testID="repostBtn"
|
testID="repostBtn"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
requireAuth(() => onPressToggleRepostWrapper())
|
requireAuth(() => dialogControl.open())
|
||||||
}}
|
}}
|
||||||
style={[styles.btn, !big && styles.btnPad]}
|
style={[a.flex_row, a.align_center, a.gap_xs, {padding: 5}]}
|
||||||
accessibilityRole="button"
|
hoverStyle={t.atoms.bg_contrast_25}
|
||||||
accessibilityLabel={`${
|
label={`${
|
||||||
isReposted
|
isReposted
|
||||||
? _(msg`Undo repost`)
|
? _(msg`Undo repost`)
|
||||||
: _(msg({message: 'Repost', context: 'action'}))
|
: _(msg({message: 'Repost', context: 'action'}))
|
||||||
} (${plural(repostCount || 0, {one: '# repost', other: '# reposts'})})`}
|
} (${plural(repostCount || 0, {one: '# repost', other: '# reposts'})})`}
|
||||||
accessibilityHint=""
|
shape="round"
|
||||||
hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
|
variant="ghost"
|
||||||
<RepostIcon
|
color="secondary">
|
||||||
style={
|
<Repost style={color} width={big ? 22 : 18} />
|
||||||
isReposted
|
|
||||||
? (styles.reposted as StyleProp<ViewStyle>)
|
|
||||||
: defaultControlColor
|
|
||||||
}
|
|
||||||
strokeWidth={2.4}
|
|
||||||
size={big ? 24 : 20}
|
|
||||||
/>
|
|
||||||
{typeof repostCount !== 'undefined' && repostCount > 0 ? (
|
{typeof repostCount !== 'undefined' && repostCount > 0 ? (
|
||||||
<Text
|
<Text
|
||||||
testID="repostCount"
|
testID="repostCount"
|
||||||
style={
|
style={[
|
||||||
isReposted
|
color,
|
||||||
? [s.bold, s.green3, s.f15, s.ml5]
|
big ? a.text_md : {fontSize: 15},
|
||||||
: [defaultControlColor, s.f15, s.ml5]
|
isReposted && a.font_bold,
|
||||||
}>
|
]}>
|
||||||
{repostCount}
|
{repostCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</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',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
|
@ -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[] = [
|
return hasSession ? (
|
||||||
{
|
<EventStopper onKeyDown={false}>
|
||||||
label: isReposted ? _(msg`Undo repost`) : _(msg`Repost`),
|
<Menu.Root>
|
||||||
testID: 'repostDropdownRepostBtn',
|
<Menu.Trigger label={_(msg`Repost or quote post`)}>
|
||||||
icon: {
|
{({props, state}) => {
|
||||||
ios: {name: 'repeat'},
|
return (
|
||||||
android: '',
|
<Pressable
|
||||||
web: 'retweet',
|
{...props}
|
||||||
},
|
|
||||||
onPress: onRepost,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: _(msg`Quote post`),
|
|
||||||
testID: 'repostDropdownQuoteBtn',
|
|
||||||
icon: {
|
|
||||||
ios: {name: 'quote.bubble'},
|
|
||||||
android: '',
|
|
||||||
web: 'quote-left',
|
|
||||||
},
|
|
||||||
onPress: onQuote,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const inner = (
|
|
||||||
<View
|
|
||||||
style={[
|
style={[
|
||||||
styles.btn,
|
a.rounded_full,
|
||||||
!big && styles.btnPad,
|
(state.hovered || state.pressed) && {
|
||||||
(isReposted
|
backgroundColor: t.palette.contrast_25,
|
||||||
? styles.reposted
|
},
|
||||||
: defaultControlColor) as StyleProp<ViewStyle>,
|
|
||||||
]}>
|
]}>
|
||||||
<RepostIcon strokeWidth={2.2} size={big ? 24 : 20} />
|
<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>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
requireAuth(() => {})
|
||||||
|
}}
|
||||||
|
label={_(msg`Repost or quote post`)}
|
||||||
|
style={{padding: 0}}
|
||||||
|
hoverStyle={t.atoms.bg_contrast_25}
|
||||||
|
shape="round"
|
||||||
|
variant="ghost"
|
||||||
|
color="secondary">
|
||||||
|
<RepostInner
|
||||||
|
isReposted={isReposted}
|
||||||
|
color={color}
|
||||||
|
repostCount={repostCount}
|
||||||
|
big={big}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const RepostInner = ({
|
||||||
|
isReposted,
|
||||||
|
color,
|
||||||
|
repostCount,
|
||||||
|
big,
|
||||||
|
}: {
|
||||||
|
isReposted: boolean
|
||||||
|
color: {color: string}
|
||||||
|
repostCount?: number
|
||||||
|
big?: boolean
|
||||||
|
}) => (
|
||||||
|
<View style={[a.flex_row, a.align_center, a.gap_xs, {padding: 5}]}>
|
||||||
|
<Repost style={color} width={big ? 22 : 18} />
|
||||||
{typeof repostCount !== 'undefined' && repostCount > 0 ? (
|
{typeof repostCount !== 'undefined' && repostCount > 0 ? (
|
||||||
<Text
|
<Text
|
||||||
testID="repostCount"
|
testID="repostCount"
|
||||||
type={isReposted ? 'md-bold' : 'md'}
|
style={[
|
||||||
style={styles.repostCount}>
|
color,
|
||||||
|
big ? a.text_md : {fontSize: 15},
|
||||||
|
isReposted && [a.font_bold],
|
||||||
|
a.user_select_none,
|
||||||
|
]}>
|
||||||
{repostCount}
|
{repostCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
||||||
return hasSession ? (
|
|
||||||
<EventStopper>
|
|
||||||
<NativeDropdown
|
|
||||||
items={dropdownItems}
|
|
||||||
accessibilityLabel={_(msg`Repost or quote post`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
{inner}
|
|
||||||
</NativeDropdown>
|
|
||||||
</EventStopper>
|
|
||||||
) : (
|
|
||||||
<Pressable
|
|
||||||
accessibilityRole="button"
|
|
||||||
onPress={() => {
|
|
||||||
requireAuth(() => {})
|
|
||||||
}}
|
|
||||||
accessibilityLabel={_(msg`Repost or quote post`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
{inner}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
btn: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 4,
|
|
||||||
},
|
|
||||||
btnPad: {
|
|
||||||
paddingTop: 5,
|
|
||||||
paddingBottom: 5,
|
|
||||||
paddingLeft: 5,
|
|
||||||
paddingRight: 5,
|
|
||||||
},
|
|
||||||
reposted: {
|
|
||||||
color: colors.green3,
|
|
||||||
},
|
|
||||||
repostCount: {
|
|
||||||
color: 'currentColor',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|