diff options
Diffstat (limited to 'src/view/com/util')
-rw-r--r-- | src/view/com/util/List.web.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/MainScrollProvider.tsx | 16 | ||||
-rw-r--r-- | src/view/com/util/forms/NativeDropdown.tsx | 3 | ||||
-rw-r--r-- | src/view/com/util/forms/NativeDropdown.web.tsx | 12 | ||||
-rw-r--r-- | src/view/com/util/forms/PostDropdownBtn.tsx | 16 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/QuoteEmbed.tsx | 2 | ||||
-rw-r--r-- | src/view/com/util/text/RichText.tsx | 66 |
7 files changed, 113 insertions, 6 deletions
diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index 29bad2db8..936bac198 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -172,7 +172,7 @@ function ListImpl<ItemT>( <View ref={containerRef} style={[ - styles.contentContainer, + !isMobile && styles.sideBorders, contentContainerStyle, desktopFixedHeight ? styles.minHeightViewport : null, pal.border, @@ -304,7 +304,7 @@ export const List = memo(React.forwardRef(ListImpl)) as <ItemT>( const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) const styles = StyleSheet.create({ - contentContainer: { + sideBorders: { borderLeftWidth: 1, borderRightWidth: 1, }, diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx index 2c90e33ff..01b8a954d 100644 --- a/src/view/com/util/MainScrollProvider.tsx +++ b/src/view/com/util/MainScrollProvider.tsx @@ -20,12 +20,14 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { const setMode = useSetMinimalShellMode() const startDragOffset = useSharedValue<number | null>(null) const startMode = useSharedValue<number | null>(null) + const didJustRestoreScroll = useSharedValue<boolean>(false) useEffect(() => { if (isWeb) { return listenToForcedWindowScroll(() => { startDragOffset.value = null startMode.value = null + didJustRestoreScroll.value = true }) } }) @@ -86,6 +88,11 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { mode.value = newValue } } else { + if (didJustRestoreScroll.value) { + didJustRestoreScroll.value = false + // Don't hide/show navbar based on scroll restoratoin. + return + } // On the web, we don't try to follow the drag because we don't know when it ends. // Instead, show/hide immediately based on whether we're scrolling up or down. const dy = e.contentOffset.y - (startDragOffset.value ?? 0) @@ -98,7 +105,14 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) { } } }, - [headerHeight, mode, setMode, startDragOffset, startMode], + [ + headerHeight, + mode, + setMode, + startDragOffset, + startMode, + didJustRestoreScroll, + ], ) return ( diff --git a/src/view/com/util/forms/NativeDropdown.tsx b/src/view/com/util/forms/NativeDropdown.tsx index 082285064..0a47569f2 100644 --- a/src/view/com/util/forms/NativeDropdown.tsx +++ b/src/view/com/util/forms/NativeDropdown.tsx @@ -1,7 +1,7 @@ import React from 'react' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import * as DropdownMenu from 'zeego/dropdown-menu' -import {Pressable, StyleSheet, Platform, View} from 'react-native' +import {Pressable, StyleSheet, Platform, View, ViewStyle} from 'react-native' import {IconProp} from '@fortawesome/fontawesome-svg-core' import {MenuItemCommonProps} from 'zeego/lib/typescript/menu' import {usePalette} from 'lib/hooks/usePalette' @@ -151,6 +151,7 @@ type Props = { testID?: string accessibilityLabel?: string accessibilityHint?: string + triggerStyle?: ViewStyle } /* The `NativeDropdown` function uses native iOS and Android dropdown menus. diff --git a/src/view/com/util/forms/NativeDropdown.web.tsx b/src/view/com/util/forms/NativeDropdown.web.tsx index 9e9888ad8..6abeb16cc 100644 --- a/src/view/com/util/forms/NativeDropdown.web.tsx +++ b/src/view/com/util/forms/NativeDropdown.web.tsx @@ -1,7 +1,7 @@ import React from 'react' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import * as DropdownMenu from '@radix-ui/react-dropdown-menu' -import {Pressable, StyleSheet, View, Text} from 'react-native' +import {Pressable, StyleSheet, View, Text, ViewStyle} from 'react-native' import {IconProp} from '@fortawesome/fontawesome-svg-core' import {MenuItemCommonProps} from 'zeego/lib/typescript/menu' import {usePalette} from 'lib/hooks/usePalette' @@ -21,6 +21,7 @@ export const DropdownMenuItem = (props: ItemProps & {testID?: string}) => { return ( <DropdownMenu.Item + className="nativeDropdown-item" {...props} style={StyleSheet.flatten([ styles.item, @@ -52,6 +53,7 @@ type Props = { testID?: string accessibilityLabel?: string accessibilityHint?: string + triggerStyle?: ViewStyle } export function NativeDropdown({ @@ -60,6 +62,7 @@ export function NativeDropdown({ testID, accessibilityLabel, accessibilityHint, + triggerStyle, }: React.PropsWithChildren<Props>) { const pal = usePalette('default') const theme = useTheme() @@ -119,7 +122,8 @@ export function NativeDropdown({ accessibilityLabel={accessibilityLabel} accessibilityHint={accessibilityHint} onPress={() => setOpen(o => !o)} - hitSlop={HITSLOP_10}> + hitSlop={HITSLOP_10} + style={triggerStyle}> {children} </Pressable> </DropdownMenu.Trigger> @@ -232,6 +236,10 @@ const styles = StyleSheet.create({ paddingLeft: 12, paddingRight: 12, borderRadius: 8, + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', + outline: 0, + border: 0, }, itemTitle: { fontSize: 16, diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 1dfb687df..09850a7f5 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -34,6 +34,7 @@ import {useLingui} from '@lingui/react' import {useSession} from '#/state/session' import {isWeb} from '#/platform/detection' import {richTextToString} from '#/lib/strings/rich-text-helpers' +import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' let PostDropdownBtn = ({ testID, @@ -67,6 +68,7 @@ let PostDropdownBtn = ({ const {hidePost} = useHiddenPostsApi() const openLink = useOpenLink() const navigation = useNavigation() + const {mutedWordsDialogControl} = useGlobalDialogsControlContext() const rootUri = record.reply?.root?.uri || postUri const isThreadMuted = mutedThreads.includes(rootUri) @@ -210,6 +212,20 @@ let PostDropdownBtn = ({ web: 'comment-slash', }, }, + hasSession && { + label: _(msg`Mute words & tags`), + onPress() { + mutedWordsDialogControl.open() + }, + testID: 'postDropdownMuteWordsBtn', + icon: { + ios: { + name: 'speaker.slash', + }, + android: 'ic_lock_silent_mode', + web: 'filter', + }, + }, hasSession && !isAuthor && !isPostHidden && { diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx index c128a6f00..35b091269 100644 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx @@ -128,10 +128,12 @@ export function QuoteEmbed({ ) : null} {richText ? ( <RichText + enableTags value={richText} style={[a.text_md]} numberOfLines={20} disableLinks + authorHandle={quote.author.handle} /> ) : null} {embed && <PostEmbeds embed={embed} moderation={{}} />} diff --git a/src/view/com/util/text/RichText.tsx b/src/view/com/util/text/RichText.tsx index b6d461224..0ec3f3181 100644 --- a/src/view/com/util/text/RichText.tsx +++ b/src/view/com/util/text/RichText.tsx @@ -7,6 +7,9 @@ import {lh} from 'lib/styles' import {toShortUrl} from 'lib/strings/url-helpers' import {useTheme, TypographyVariant} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' +import {makeTagLink} from 'lib/routes/links' +import {TagMenu, useTagMenuControl} from '#/components/TagMenu' +import {isNative} from '#/platform/detection' const WORD_WRAP = {wordWrap: 1} @@ -82,6 +85,7 @@ export function RichText({ for (const segment of richText.segments()) { const link = segment.link const mention = segment.mention + const tag = segment.tag if ( !noLinks && mention && @@ -115,6 +119,21 @@ export function RichText({ />, ) } + } else if ( + !noLinks && + tag && + AppBskyRichtextFacet.validateTag(tag).success + ) { + els.push( + <RichTextTag + key={key} + text={segment.text} + type={type} + style={style} + lineHeightStyle={lineHeightStyle} + selectable={selectable} + />, + ) } else { els.push(segment.text) } @@ -133,3 +152,50 @@ export function RichText({ </Text> ) } + +function RichTextTag({ + text: tag, + type, + style, + lineHeightStyle, + selectable, +}: { + text: string + type?: TypographyVariant + style?: StyleProp<TextStyle> + lineHeightStyle?: TextStyle + selectable?: boolean +}) { + const pal = usePalette('default') + const control = useTagMenuControl() + + const open = React.useCallback(() => { + control.open() + }, [control]) + + return ( + <React.Fragment> + <TagMenu control={control} tag={tag}> + {isNative ? ( + <TextLink + type={type} + text={tag} + // segment.text has the leading "#" while tag.tag does not + href={makeTagLink(tag)} + style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]} + dataSet={WORD_WRAP} + selectable={selectable} + onPress={open} + /> + ) : ( + <Text + selectable={selectable} + type={type} + style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]}> + {tag} + </Text> + )} + </TagMenu> + </React.Fragment> + ) +} |