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