diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-09-02 15:43:10 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-09-02 15:43:10 -0500 |
commit | 41bbe2b60bf37f52ab8404a275a523c4b4b97a15 (patch) | |
tree | ef72fbaf96813582aa0976cc6a61a72683f6eede /src | |
parent | cdae685ee12a0d7807c911a3116eeafd0a8307f5 (diff) | |
download | voidsky-41bbe2b60bf37f52ab8404a275a523c4b4b97a15.tar.zst |
Add post dropdown menus
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/feed/FeedItem.tsx | 20 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 23 | ||||
-rw-r--r-- | src/view/com/util/DropdownBtn.tsx | 177 | ||||
-rw-r--r-- | src/view/shell/mobile/location-menu.tsx | 2 |
4 files changed, 216 insertions, 6 deletions
diff --git a/src/view/com/feed/FeedItem.tsx b/src/view/com/feed/FeedItem.tsx index 0361925cb..52d162a62 100644 --- a/src/view/com/feed/FeedItem.tsx +++ b/src/view/com/feed/FeedItem.tsx @@ -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]}> · {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} diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 5909cac8a..53ae8e548 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -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]}> · {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]}> · {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} diff --git a/src/view/com/util/DropdownBtn.tsx b/src/view/com/util/DropdownBtn.tsx new file mode 100644 index 000000000..825a5395c --- /dev/null +++ b/src/view/com/util/DropdownBtn.tsx @@ -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, + }, +}) diff --git a/src/view/shell/mobile/location-menu.tsx b/src/view/shell/mobile/location-menu.tsx index fc851f77a..598d82c3e 100644 --- a/src/view/shell/mobile/location-menu.tsx +++ b/src/view/shell/mobile/location-menu.tsx @@ -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) => { |