Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

Select the types of activity you want to include in your feed.

at a876aae44ea07494ebea9727350aa060b81f317b 173 lines 5.5 kB view raw
1import {type StyleProp, Text as RNText, type TextStyle} from 'react-native' 2import {msg} from '@lingui/core/macro' 3import {useLingui} from '@lingui/react' 4import {Trans} from '@lingui/react/macro' 5import {useNavigation} from '@react-navigation/native' 6 7import {type NavigationProp} from '#/lib/routes/types' 8import {isInvalidHandle} from '#/lib/strings/handles' 9import { 10 usePreferencesQuery, 11 useRemoveMutedWordsMutation, 12 useUpsertMutedWordsMutation, 13} from '#/state/queries/preferences' 14import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass' 15import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' 16import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person' 17import { 18 createStaticClick, 19 createStaticClickIfUnmodified, 20 InlineLinkText, 21} from '#/components/Link' 22import {Loader} from '#/components/Loader' 23import * as Menu from '#/components/Menu' 24import {IS_NATIVE, IS_WEB} from '#/env' 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 isCashtag = tag.startsWith('$') 52 const label = isCashtag ? _(msg`Cashtag ${tag}`) : _(msg`Hashtag ${tag}`) 53 const hint = IS_NATIVE 54 ? _(msg`Long press to open tag menu for ${isCashtag ? tag : `#${tag}`}`) 55 : _(msg`Click to open tag menu for ${isCashtag ? tag : `#${tag}`}`) 56 57 const isMuted = Boolean( 58 (preferences?.moderationPrefs.mutedWords?.find( 59 m => m.value === tag && m.targets.includes('tag'), 60 ) ?? 61 optimisticUpsert?.find( 62 m => m.value === tag && m.targets.includes('tag'), 63 )) && 64 !optimisticRemove?.find(m => m?.value === tag), 65 ) 66 67 /* 68 * Mute word records that exactly match the tag in question. 69 */ 70 const removeableMuteWords = 71 preferences?.moderationPrefs.mutedWords?.filter(word => { 72 return word.value === tag 73 }) || [] 74 75 return ( 76 <Menu.Root> 77 <Menu.Trigger label={label} hint={hint}> 78 {({props: menuProps}) => ( 79 <InlineLinkText 80 to={{ 81 screen: 'Hashtag', 82 params: {tag: encodeURIComponent(tag)}, 83 }} 84 {...menuProps} 85 onPress={e => { 86 if (IS_WEB) { 87 return createStaticClickIfUnmodified(() => { 88 if (!IS_NATIVE) { 89 menuProps.onPress() 90 } 91 }).onPress(e) 92 } 93 }} 94 onLongPress={createStaticClick(menuProps.onPress).onPress} 95 accessibilityHint={hint} 96 label={label} 97 style={textStyle} 98 emoji> 99 {IS_NATIVE ? ( 100 display 101 ) : ( 102 <RNText ref={menuProps.ref}>{display}</RNText> 103 )} 104 </InlineLinkText> 105 )} 106 </Menu.Trigger> 107 <Menu.Outer> 108 <Menu.Group> 109 <Menu.Item 110 label={_(msg`See ${isCashtag ? tag : `#${tag}`} posts`)} 111 onPress={() => { 112 navigation.push('Hashtag', { 113 tag: encodeURIComponent(tag), 114 }) 115 }}> 116 <Menu.ItemText> 117 {isCashtag ? ( 118 <Trans>See {tag} posts</Trans> 119 ) : ( 120 <Trans>See #{tag} posts</Trans> 121 )} 122 </Menu.ItemText> 123 <Menu.ItemIcon icon={Search} /> 124 </Menu.Item> 125 {authorHandle && !isInvalidHandle(authorHandle) && ( 126 <Menu.Item 127 label={_(msg`See ${isCashtag ? tag : `#${tag}`} posts by user`)} 128 onPress={() => { 129 navigation.push('Hashtag', { 130 tag: encodeURIComponent(tag), 131 author: authorHandle, 132 }) 133 }}> 134 <Menu.ItemText> 135 {isCashtag ? ( 136 <Trans>See {tag} posts by user</Trans> 137 ) : ( 138 <Trans>See #{tag} posts by user</Trans> 139 )} 140 </Menu.ItemText> 141 <Menu.ItemIcon icon={Person} /> 142 </Menu.Item> 143 )} 144 </Menu.Group> 145 <Menu.Divider /> 146 <Menu.Item 147 label={ 148 isMuted 149 ? _(msg`Unmute ${isCashtag ? tag : `#${tag}`}`) 150 : _(msg`Mute ${isCashtag ? tag : `#${tag}`}`) 151 } 152 onPress={() => { 153 if (isMuted) { 154 resetUpsert() 155 removeMutedWords(removeableMuteWords) 156 } else { 157 resetRemove() 158 upsertMutedWord([ 159 {value: tag, targets: ['tag'], actorTarget: 'all'}, 160 ]) 161 } 162 }}> 163 <Menu.ItemText> 164 {isMuted 165 ? _(msg`Unmute ${isCashtag ? tag : `#${tag}`}`) 166 : _(msg`Mute ${isCashtag ? tag : `#${tag}`}`)} 167 </Menu.ItemText> 168 <Menu.ItemIcon icon={isPreferencesLoading ? Loader : Mute} /> 169 </Menu.Item> 170 </Menu.Outer> 171 </Menu.Root> 172 ) 173}