Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at post-text-option 296 lines 9.9 kB view raw
1import {type AppBskyActorDefs, AppBskyGraphDefs, AtUri} from '@atproto/api' 2import {msg, Trans} from '@lingui/macro' 3import {useLingui} from '@lingui/react' 4import {useNavigation} from '@react-navigation/native' 5 6import {type NavigationProp} from '#/lib/routes/types' 7import {shareUrl} from '#/lib/sharing' 8import {toShareUrl} from '#/lib/strings/url-helpers' 9import {logger} from '#/logger' 10import {isWeb} from '#/platform/detection' 11import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 12import { 13 useListBlockMutation, 14 useListDeleteMutation, 15 useListMuteMutation, 16} from '#/state/queries/list' 17import {useRemoveFeedMutation} from '#/state/queries/preferences' 18import {useSession} from '#/state/session' 19import {Button, ButtonIcon} from '#/components/Button' 20import {useDialogControl} from '#/components/Dialog' 21import {CreateOrEditListDialog} from '#/components/dialogs/lists/CreateOrEditListDialog' 22import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ShareIcon} from '#/components/icons/ArrowOutOfBox' 23import {ChainLink_Stroke2_Corner0_Rounded as ChainLink} from '#/components/icons/ChainLink' 24import {DotGrid_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid' 25import {PencilLine_Stroke2_Corner0_Rounded as PencilLineIcon} from '#/components/icons/Pencil' 26import {PersonCheck_Stroke2_Corner0_Rounded as PersonCheckIcon} from '#/components/icons/Person' 27import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin' 28import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' 29import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' 30import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' 31import * as Menu from '#/components/Menu' 32import { 33 ReportDialog, 34 useReportDialogControl, 35} from '#/components/moderation/ReportDialog' 36import * as Prompt from '#/components/Prompt' 37import * as Toast from '#/components/Toast' 38 39export function MoreOptionsMenu({ 40 list, 41 savedFeedConfig, 42}: { 43 list: AppBskyGraphDefs.ListView 44 savedFeedConfig?: AppBskyActorDefs.SavedFeed 45}) { 46 const {_} = useLingui() 47 const {currentAccount} = useSession() 48 const editListDialogControl = useDialogControl() 49 const deleteListPromptControl = useDialogControl() 50 const reportDialogControl = useReportDialogControl() 51 const navigation = useNavigation<NavigationProp>() 52 53 const {mutateAsync: removeSavedFeed} = useRemoveFeedMutation() 54 const {mutateAsync: deleteList} = useListDeleteMutation() 55 const {mutateAsync: muteList} = useListMuteMutation() 56 const {mutateAsync: blockList} = useListBlockMutation() 57 58 const isCurateList = list.purpose === AppBskyGraphDefs.CURATELIST 59 const isModList = list.purpose === AppBskyGraphDefs.MODLIST 60 const isBlocking = !!list.viewer?.blocked 61 const isMuting = !!list.viewer?.muted 62 const isPinned = Boolean(savedFeedConfig?.pinned) 63 const isOwner = currentAccount?.did === list.creator.did 64 65 const enableSquareButtons = useEnableSquareButtons() 66 67 const onPressShare = () => { 68 const {rkey} = new AtUri(list.uri) 69 const url = toShareUrl(`/profile/${list.creator.did}/lists/${rkey}`) 70 shareUrl(url) 71 } 72 73 const onRemoveFromSavedFeeds = async () => { 74 if (!savedFeedConfig) return 75 try { 76 await removeSavedFeed(savedFeedConfig) 77 Toast.show(_(msg`Removed from your feeds`)) 78 } catch (e) { 79 Toast.show(_(msg`There was an issue contacting the server`), { 80 type: 'error', 81 }) 82 logger.error('Failed to remove pinned list', {message: e}) 83 } 84 } 85 86 const onPressDelete = async () => { 87 await deleteList({uri: list.uri}) 88 89 if (savedFeedConfig) { 90 await removeSavedFeed(savedFeedConfig) 91 } 92 93 Toast.show(_(msg({message: 'List deleted', context: 'toast'}))) 94 if (navigation.canGoBack()) { 95 navigation.goBack() 96 } else { 97 navigation.navigate('Home') 98 } 99 } 100 101 const onUnpinModList = async () => { 102 try { 103 if (!savedFeedConfig) return 104 await removeSavedFeed(savedFeedConfig) 105 Toast.show(_(msg`Unpinned list`)) 106 } catch { 107 Toast.show(_(msg`Failed to unpin list`), { 108 type: 'error', 109 }) 110 } 111 } 112 113 const onUnsubscribeMute = async () => { 114 try { 115 await muteList({uri: list.uri, mute: false}) 116 Toast.show(_(msg({message: 'List unmuted', context: 'toast'}))) 117 logger.metric( 118 'moderation:unsubscribedFromList', 119 {listType: 'mute'}, 120 {statsig: true}, 121 ) 122 } catch { 123 Toast.show( 124 _( 125 msg`There was an issue. Please check your internet connection and try again.`, 126 ), 127 ) 128 } 129 } 130 131 const onUnsubscribeBlock = async () => { 132 try { 133 await blockList({uri: list.uri, block: false}) 134 Toast.show(_(msg({message: 'List unblocked', context: 'toast'}))) 135 logger.metric( 136 'moderation:unsubscribedFromList', 137 {listType: 'block'}, 138 {statsig: true}, 139 ) 140 } catch { 141 Toast.show( 142 _( 143 msg`There was an issue. Please check your internet connection and try again.`, 144 ), 145 ) 146 } 147 } 148 149 return ( 150 <> 151 <Menu.Root> 152 <Menu.Trigger label={_(msg`More options`)}> 153 {({props}) => ( 154 <Button 155 label={props.accessibilityLabel} 156 testID="moreOptionsBtn" 157 size="small" 158 color="secondary" 159 shape={enableSquareButtons ? 'square' : 'round'} 160 {...props}> 161 <ButtonIcon icon={DotGridIcon} /> 162 </Button> 163 )} 164 </Menu.Trigger> 165 <Menu.Outer> 166 <Menu.Group> 167 <Menu.Item 168 label={isWeb ? _(msg`Copy link to list`) : _(msg`Share via...`)} 169 onPress={onPressShare}> 170 <Menu.ItemText> 171 {isWeb ? ( 172 <Trans>Copy link to list</Trans> 173 ) : ( 174 <Trans>Share via...</Trans> 175 )} 176 </Menu.ItemText> 177 <Menu.ItemIcon 178 position="right" 179 icon={isWeb ? ChainLink : ShareIcon} 180 /> 181 </Menu.Item> 182 {savedFeedConfig && ( 183 <Menu.Item 184 label={_(msg`Remove from my feeds`)} 185 onPress={onRemoveFromSavedFeeds}> 186 <Menu.ItemText> 187 <Trans>Remove from my feeds</Trans> 188 </Menu.ItemText> 189 <Menu.ItemIcon position="right" icon={TrashIcon} /> 190 </Menu.Item> 191 )} 192 </Menu.Group> 193 194 <Menu.Divider /> 195 196 {isOwner ? ( 197 <Menu.Group> 198 <Menu.Item 199 label={_(msg`Edit list details`)} 200 onPress={editListDialogControl.open}> 201 <Menu.ItemText> 202 <Trans>Edit list details</Trans> 203 </Menu.ItemText> 204 <Menu.ItemIcon position="right" icon={PencilLineIcon} /> 205 </Menu.Item> 206 <Menu.Item 207 label={_(msg`Delete list`)} 208 onPress={deleteListPromptControl.open}> 209 <Menu.ItemText> 210 <Trans>Delete list</Trans> 211 </Menu.ItemText> 212 <Menu.ItemIcon position="right" icon={TrashIcon} /> 213 </Menu.Item> 214 </Menu.Group> 215 ) : ( 216 <Menu.Group> 217 <Menu.Item 218 label={_(msg`Report list`)} 219 onPress={reportDialogControl.open}> 220 <Menu.ItemText> 221 <Trans>Report list</Trans> 222 </Menu.ItemText> 223 <Menu.ItemIcon position="right" icon={WarningIcon} /> 224 </Menu.Item> 225 </Menu.Group> 226 )} 227 228 {isModList && isPinned && ( 229 <> 230 <Menu.Divider /> 231 <Menu.Group> 232 <Menu.Item 233 label={_(msg`Unpin moderation list`)} 234 onPress={onUnpinModList}> 235 <Menu.ItemText> 236 <Trans>Unpin moderation list</Trans> 237 </Menu.ItemText> 238 <Menu.ItemIcon icon={PinIcon} /> 239 </Menu.Item> 240 </Menu.Group> 241 </> 242 )} 243 244 {isCurateList && (isBlocking || isMuting) && ( 245 <> 246 <Menu.Divider /> 247 <Menu.Group> 248 {isBlocking && ( 249 <Menu.Item 250 label={_(msg`Unblock list`)} 251 onPress={onUnsubscribeBlock}> 252 <Menu.ItemText> 253 <Trans>Unblock list</Trans> 254 </Menu.ItemText> 255 <Menu.ItemIcon icon={PersonCheckIcon} /> 256 </Menu.Item> 257 )} 258 {isMuting && ( 259 <Menu.Item 260 label={_(msg`Unmute list`)} 261 onPress={onUnsubscribeMute}> 262 <Menu.ItemText> 263 <Trans>Unmute list</Trans> 264 </Menu.ItemText> 265 <Menu.ItemIcon icon={UnmuteIcon} /> 266 </Menu.Item> 267 )} 268 </Menu.Group> 269 </> 270 )} 271 </Menu.Outer> 272 </Menu.Root> 273 274 <CreateOrEditListDialog control={editListDialogControl} list={list} /> 275 276 <Prompt.Basic 277 control={deleteListPromptControl} 278 title={_(msg`Delete this list?`)} 279 description={_( 280 msg`If you delete this list, you won't be able to recover it.`, 281 )} 282 onConfirm={onPressDelete} 283 confirmButtonCta={_(msg`Delete`)} 284 confirmButtonColor="negative" 285 /> 286 287 <ReportDialog 288 control={reportDialogControl} 289 subject={{ 290 ...list, 291 $type: 'app.bsky.graph.defs#listView', 292 }} 293 /> 294 </> 295 ) 296}