forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2import {type StyleProp, Text as RNText, type TextStyle} from 'react-native'
3import {msg, Trans} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5import {useNavigation} from '@react-navigation/native'
6
7import {type NavigationProp} from '#/lib/routes/types'
8import {isInvalidHandle} from '#/lib/strings/handles'
9import {isNative, isWeb} from '#/platform/detection'
10import {
11 usePreferencesQuery,
12 useRemoveMutedWordsMutation,
13 useUpsertMutedWordsMutation,
14} from '#/state/queries/preferences'
15import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
16import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
17import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
18import {
19 createStaticClick,
20 createStaticClickIfUnmodified,
21 InlineLinkText,
22} from '#/components/Link'
23import {Loader} from '#/components/Loader'
24import * as Menu from '#/components/Menu'
25
26export function RichTextTag({
27 tag,
28 display,
29 authorHandle,
30 textStyle,
31}: {
32 tag: string
33 display: string
34 authorHandle?: string
35 textStyle: StyleProp<TextStyle>
36}) {
37 const {_} = useLingui()
38 const {isLoading: isPreferencesLoading, data: preferences} =
39 usePreferencesQuery()
40 const {
41 mutateAsync: upsertMutedWord,
42 variables: optimisticUpsert,
43 reset: resetUpsert,
44 } = useUpsertMutedWordsMutation()
45 const {
46 mutateAsync: removeMutedWords,
47 variables: optimisticRemove,
48 reset: resetRemove,
49 } = useRemoveMutedWordsMutation()
50 const navigation = useNavigation<NavigationProp>()
51 const label = _(msg`Hashtag ${tag}`)
52 const hint = isNative
53 ? _(msg`Long press to open tag menu for #${tag}`)
54 : _(msg`Click to open tag menu for ${tag}`)
55
56 const isMuted = Boolean(
57 (preferences?.moderationPrefs.mutedWords?.find(
58 m => m.value === tag && m.targets.includes('tag'),
59 ) ??
60 optimisticUpsert?.find(
61 m => m.value === tag && m.targets.includes('tag'),
62 )) &&
63 !optimisticRemove?.find(m => m?.value === tag),
64 )
65
66 /*
67 * Mute word records that exactly match the tag in question.
68 */
69 const removeableMuteWords = React.useMemo(() => {
70 return (
71 preferences?.moderationPrefs.mutedWords?.filter(word => {
72 return word.value === tag
73 }) || []
74 )
75 }, [tag, preferences?.moderationPrefs?.mutedWords])
76
77 return (
78 <Menu.Root>
79 <Menu.Trigger label={label} hint={hint}>
80 {({props: menuProps}) => (
81 <InlineLinkText
82 to={{
83 screen: 'Hashtag',
84 params: {tag: encodeURIComponent(tag)},
85 }}
86 {...menuProps}
87 onPress={e => {
88 if (isWeb) {
89 return createStaticClickIfUnmodified(() => {
90 if (!isNative) {
91 menuProps.onPress()
92 }
93 }).onPress(e)
94 }
95 }}
96 onLongPress={createStaticClick(menuProps.onPress).onPress}
97 accessibilityHint={hint}
98 label={label}
99 style={textStyle}
100 emoji>
101 {isNative ? (
102 display
103 ) : (
104 <RNText ref={menuProps.ref}>{display}</RNText>
105 )}
106 </InlineLinkText>
107 )}
108 </Menu.Trigger>
109 <Menu.Outer>
110 <Menu.Group>
111 <Menu.Item
112 label={_(msg`See ${tag} skeets`)}
113 onPress={() => {
114 navigation.push('Hashtag', {
115 tag: encodeURIComponent(tag),
116 })
117 }}>
118 <Menu.ItemText>
119 <Trans>See #{tag} skeets</Trans>
120 </Menu.ItemText>
121 <Menu.ItemIcon icon={Search} />
122 </Menu.Item>
123 {authorHandle && !isInvalidHandle(authorHandle) && (
124 <Menu.Item
125 label={_(msg`See ${tag} skeets by user`)}
126 onPress={() => {
127 navigation.push('Hashtag', {
128 tag: encodeURIComponent(tag),
129 author: authorHandle,
130 })
131 }}>
132 <Menu.ItemText>
133 <Trans>See #{tag} skeets by user</Trans>
134 </Menu.ItemText>
135 <Menu.ItemIcon icon={Person} />
136 </Menu.Item>
137 )}
138 </Menu.Group>
139 <Menu.Divider />
140 <Menu.Item
141 label={isMuted ? _(msg`Unmute ${tag}`) : _(msg`Mute ${tag}`)}
142 onPress={() => {
143 if (isMuted) {
144 resetUpsert()
145 removeMutedWords(removeableMuteWords)
146 } else {
147 resetRemove()
148 upsertMutedWord([
149 {value: tag, targets: ['tag'], actorTarget: 'all'},
150 ])
151 }
152 }}>
153 <Menu.ItemText>
154 {isMuted ? _(msg`Unmute ${tag}`) : _(msg`Mute ${tag}`)}
155 </Menu.ItemText>
156 <Menu.ItemIcon icon={isPreferencesLoading ? Loader : Mute} />
157 </Menu.Item>
158 </Menu.Outer>
159 </Menu.Root>
160 )
161}