Add post dropdown menus

zio/stable
Paul Frazee 2022-09-02 15:43:10 -05:00
parent cdae685ee1
commit 41bbe2b60b
4 changed files with 216 additions and 6 deletions

View File

@ -5,6 +5,7 @@ import {bsky, AdxUri} from '@adxp/mock-api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {FeedViewItemModel} from '../../../state/models/feed-view'
import {Link} from '../util/Link'
import {PostDropdownBtn} from '../util/DropdownBtn'
import {s, colors} from '../../lib/styles'
import {ago} from '../../lib/strings'
import {AVIS} from '../../lib/assets'
@ -19,10 +20,11 @@ export const FeedItem = observer(function FeedItem({
}) {
const store = useStores()
const record = item.record as unknown as bsky.Post.Record
const postHref = useMemo(() => {
const itemHref = useMemo(() => {
const urip = new AdxUri(item.uri)
return `/profile/${item.author.name}/post/${urip.recordKey}`
}, [item.uri, item.author.name])
const itemTitle = `Post by ${item.author.name}`
const authorHref = `/profile/${item.author.name}`
const onPressReply = () => {
@ -40,10 +42,7 @@ export const FeedItem = observer(function FeedItem({
}
return (
<Link
style={styles.outer}
href={postHref}
title={`Post by ${item.author.name}`}>
<Link style={styles.outer} href={itemHref} title={itemTitle}>
{item.repostedBy && (
<View style={styles.repostedBy}>
<FontAwesomeIcon icon="retweet" style={styles.repostedByIcon} />
@ -79,6 +78,17 @@ export const FeedItem = observer(function FeedItem({
<Text style={[styles.metaItem, s.f14, s.gray5]}>
&middot; {ago(item.indexedAt)}
</Text>
<View style={s.flex1} />
<PostDropdownBtn
style={styles.metaItem}
itemHref={itemHref}
itemTitle={itemTitle}>
<FontAwesomeIcon
icon="ellipsis-h"
size={14}
style={[s.mt2, s.mr5]}
/>
</PostDropdownBtn>
</View>
<Text style={[styles.postText, s.f15, s['lh15-1.3']]}>
{record.text}

View File

@ -5,6 +5,7 @@ import {bsky, AdxUri} from '@adxp/mock-api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {PostThreadViewPostModel} from '../../../state/models/post-thread-view'
import {Link} from '../util/Link'
import {PostDropdownBtn} from '../util/DropdownBtn'
import {s, colors} from '../../lib/styles'
import {ago, pluralize} from '../../lib/strings'
import {AVIS} from '../../lib/assets'
@ -119,6 +120,17 @@ export const PostThreadItem = observer(function PostThreadItem({
<Text style={[styles.metaItem, s.f14, s.gray5]}>
&middot; {ago(item.indexedAt)}
</Text>
<View style={s.flex1} />
<PostDropdownBtn
style={styles.metaItem}
itemHref={itemHref}
itemTitle={itemTitle}>
<FontAwesomeIcon
icon="ellipsis-h"
size={14}
style={[s.mt2, s.mr5]}
/>
</PostDropdownBtn>
</View>
<View style={styles.meta}>
<Link
@ -199,6 +211,17 @@ export const PostThreadItem = observer(function PostThreadItem({
<Text style={[styles.metaItem, s.f14, s.gray5]}>
&middot; {ago(item.indexedAt)}
</Text>
<View style={s.flex1} />
<PostDropdownBtn
style={styles.metaItem}
itemHref={itemHref}
itemTitle={itemTitle}>
<FontAwesomeIcon
icon="ellipsis-h"
size={14}
style={[s.mt2, s.mr5]}
/>
</PostDropdownBtn>
</View>
<Text style={[styles.postText, s.f15, s['lh15-1.3']]}>
{record.text}

View File

@ -0,0 +1,177 @@
import React, {useRef} from 'react'
import {
StyleProp,
StyleSheet,
Text,
TouchableOpacity,
TouchableWithoutFeedback,
View,
ViewStyle,
} from 'react-native'
import {IconProp} from '@fortawesome/fontawesome-svg-core'
import RootSiblings from 'react-native-root-siblings'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {colors} from '../../lib/styles'
import {useStores} from '../../../state'
import {SharePostModel} from '../../../state/models/shell'
export interface DropdownItem {
icon?: IconProp
label: string
onPress: () => void
}
export function DropdownBtn({
style,
items,
menuWidth,
children,
}: {
style?: StyleProp<ViewStyle>
items: DropdownItem[]
menuWidth?: number
children?: React.ReactNode
}) {
const ref = useRef<TouchableOpacity>(null)
const onPress = () => {
ref.current?.measure(
(
_x: number,
_y: number,
width: number,
height: number,
pageX: number,
pageY: number,
) => {
if (!menuWidth) {
menuWidth = 200
}
createDropdownMenu(
pageX + width - menuWidth,
pageY + height,
menuWidth,
items,
)
},
)
}
return (
<TouchableOpacity style={style} onPress={onPress} ref={ref}>
{children}
</TouchableOpacity>
)
}
export function PostDropdownBtn({
style,
children,
itemHref,
itemTitle,
}: {
style?: StyleProp<ViewStyle>
children?: React.ReactNode
itemHref: string
itemTitle: string
}) {
const store = useStores()
const dropdownItems: DropdownItem[] = [
{
icon: ['far', 'clone'],
label: 'Open in new tab',
onPress() {
store.nav.newTab(itemHref)
},
},
{
icon: 'share',
label: 'Share...',
onPress() {
store.shell.openModal(new SharePostModel(itemHref))
},
},
]
return (
<DropdownBtn style={style} items={dropdownItems} menuWidth={200}>
{children}
</DropdownBtn>
)
}
function createDropdownMenu(
x: number,
y: number,
width: number,
items: DropdownItem[],
): RootSiblings {
const onPressItem = (index: number) => {
sibling.destroy()
items[index].onPress()
}
const onOuterPress = () => sibling.destroy()
const sibling = new RootSiblings(
(
<>
<TouchableWithoutFeedback onPress={onOuterPress}>
<View style={styles.bg} />
</TouchableWithoutFeedback>
<View style={[styles.menu, {left: x, top: y, width}]}>
{items.map((item, index) => (
<TouchableOpacity
key={index}
style={[styles.menuItem]}
onPress={() => onPressItem(index)}>
{item.icon && (
<FontAwesomeIcon style={styles.icon} icon={item.icon} />
)}
<Text style={styles.label}>{item.label}</Text>
</TouchableOpacity>
))}
</View>
</>
),
)
return sibling
}
const styles = StyleSheet.create({
bg: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: '#000',
opacity: 0.1,
},
menu: {
position: 'absolute',
backgroundColor: '#fff',
borderRadius: 14,
opacity: 1,
paddingVertical: 6,
},
menuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingLeft: 10,
paddingRight: 30,
},
menuItemBorder: {
borderTopWidth: 1,
borderTopColor: colors.gray1,
marginTop: 4,
paddingTop: 12,
},
icon: {
marginLeft: 6,
marginRight: 8,
},
label: {
fontSize: 15,
},
})

View File

@ -8,7 +8,7 @@ import {
} from 'react-native'
import RootSiblings from 'react-native-root-siblings'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {s, colors} from '../../lib/styles'
import {colors} from '../../lib/styles'
export function createLocationMenu(): RootSiblings {
const onPressItem = (_index: number) => {