From 58aaad704aa971c5ebbf5a5f330a2e2129b557f6 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Mon, 26 Feb 2024 22:33:48 -0600 Subject: Add tags and mute words (#2968) * Add bare minimum hashtags support (#2804) * Add bare minimum hashtags support As atproto/api already parses hashtags, this is as simple as hooking it up like link segments. This is "bare minimum" because: - Opening hashtag "#foo" is actually just a search for "foo" right now to work around #2491. - There is no integration in the composer. This hasn't stopped people from using hashtags already, and can be added later. - This change itself only had to hook things up - thank you for having already put the hashtag parsing in place. * Remove workaround for hash search not working now that it's fixed * Add RichTextTag and TagMenu * Sketch * Remove hackfix * Some cleanup * Sketch web * Mobile design * Mobile handling of tags search * Web only * Fix navigation woes * Use new callback * Hook it up * Integrate muted tags * Fix dropdown styles * Type error * Use close callback * Fix styles * Cleanup, install latest sdk * Quick muted words screen * Targets * Dir structure * Icons, list view * Move to dialog * Add removal confirmation * Swap copy * Improve checkboxees * Update matching, add tests * Moderate embeds * Create global dialogs concept again to prevent flashing * Add access from moderation screen * Highlight tags on native * Add web highlighting * Add close to web modal * Adjust close color * Rename toggles and adjust logic * Icon update * Load states * Improve regex * Improve regex * Improve regex * Revert link test * Hyphenated words * Improve matching * Enhance * Some tweaks * Muted words modal changes * Handle invalid handles, handle long tags * Remove main regex * Better test * Space/punct check drop to includes * Lowercase post text before comparison * Add better real world test case --------- Co-authored-by: Kisaragi Hiu --- src/components/RichText.tsx | 103 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) (limited to 'src/components/RichText.tsx') diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index c72fcabdd..22391cb24 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -1,11 +1,16 @@ import React from 'react' import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' -import {atoms as a, TextStyleProp, flatten} from '#/alf' +import {atoms as a, TextStyleProp, flatten, useTheme, web, native} from '#/alf' import {InlineLink} from '#/components/Link' import {Text, TextProps} from '#/components/Typography' import {toShortUrl} from 'lib/strings/url-helpers' import {getAgent} from '#/state/session' +import {TagMenu, useTagMenuControl} from '#/components/TagMenu' +import {isNative} from '#/platform/detection' +import {useInteractionState} from '#/components/hooks/useInteractionState' const WORD_WRAP = {wordWrap: 1} @@ -17,6 +22,8 @@ export function RichText({ disableLinks, resolveFacets = false, selectable, + enableTags = false, + authorHandle, }: TextStyleProp & Pick & { value: RichTextAPI | string @@ -24,6 +31,8 @@ export function RichText({ numberOfLines?: number disableLinks?: boolean resolveFacets?: boolean + enableTags?: boolean + authorHandle?: string }) { const detected = React.useRef(false) const [richText, setRichText] = React.useState(() => @@ -85,6 +94,7 @@ export function RichText({ for (const segment of richText.segments()) { const link = segment.link const mention = segment.mention + const tag = segment.tag if ( mention && AppBskyRichtextFacet.validateMention(mention).success && @@ -118,6 +128,21 @@ export function RichText({ , ) } + } else if ( + !disableLinks && + enableTags && + tag && + AppBskyRichtextFacet.validateTag(tag).success + ) { + els.push( + , + ) } else { els.push(segment.text) } @@ -136,3 +161,79 @@ export function RichText({ ) } + +function RichTextTag({ + text: tag, + style, + selectable, + authorHandle, +}: { + text: string + selectable?: boolean + authorHandle?: string +} & TextStyleProp) { + const t = useTheme() + const {_} = useLingui() + const control = useTagMenuControl() + const { + state: hovered, + onIn: onHoverIn, + onOut: onHoverOut, + } = useInteractionState() + const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() + const { + state: pressed, + onIn: onPressIn, + onOut: onPressOut, + } = useInteractionState() + + const open = React.useCallback(() => { + control.open() + }, [control]) + + /* + * N.B. On web, this is wrapped in another pressable comopnent with a11y + * labels, etc. That's why only some of these props are applied here. + */ + + return ( + + + + {tag} + + + + ) +} -- cgit 1.4.1 From 2d14d0e2dbe845edb5836199ce79928395c079a0 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 28 Feb 2024 01:35:25 +0000 Subject: Remove dangerous derived state from RichText (#3007) * Remove facet resolution from RichText * Remove derived state --- src/components/RichText.tsx | 25 ++++--------------------- src/view/screens/Storybook/Typography.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 23 deletions(-) (limited to 'src/components/RichText.tsx') diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 22391cb24..3d5f08026 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -7,7 +7,6 @@ import {atoms as a, TextStyleProp, flatten, useTheme, web, native} from '#/alf' import {InlineLink} from '#/components/Link' import {Text, TextProps} from '#/components/Typography' import {toShortUrl} from 'lib/strings/url-helpers' -import {getAgent} from '#/state/session' import {TagMenu, useTagMenuControl} from '#/components/TagMenu' import {isNative} from '#/platform/detection' import {useInteractionState} from '#/components/hooks/useInteractionState' @@ -20,7 +19,6 @@ export function RichText({ style, numberOfLines, disableLinks, - resolveFacets = false, selectable, enableTags = false, authorHandle, @@ -30,31 +28,16 @@ export function RichText({ testID?: string numberOfLines?: number disableLinks?: boolean - resolveFacets?: boolean enableTags?: boolean authorHandle?: string }) { - const detected = React.useRef(false) - const [richText, setRichText] = React.useState(() => - value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), + const richText = React.useMemo( + () => + value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), + [value], ) const styles = [a.leading_snug, flatten(style)] - React.useEffect(() => { - if (!resolveFacets) return - - async function detectFacets() { - const rt = new RichTextAPI({text: richText.text}) - await rt.detectFacets(getAgent()) - setRichText(rt) - } - - if (!detected.current) { - detected.current = true - detectFacets() - } - }, [richText, setRichText, resolveFacets]) - const {text, facets} = richText if (!facets?.length) { diff --git a/src/view/screens/Storybook/Typography.tsx b/src/view/screens/Storybook/Typography.tsx index 8ee4270b2..f0d67c528 100644 --- a/src/view/screens/Storybook/Typography.tsx +++ b/src/view/screens/Storybook/Typography.tsx @@ -22,12 +22,14 @@ export function Typography() { atoms.text_2xs -- cgit 1.4.1