Bluesky app fork with some witchin' additions 💫
at jean/pds-label 170 lines 4.9 kB view raw
1import {View} from 'react-native' 2import {msg} from '@lingui/core/macro' 3import {useLingui} from '@lingui/react' 4import {Trans} from '@lingui/react/macro' 5 6import {useCleanError} from '#/lib/hooks/useCleanError' 7import {atoms as a, web} from '#/alf' 8import {Admonition} from '#/components/Admonition' 9import {Button, ButtonIcon, ButtonText} from '#/components/Button' 10import * as Dialog from '#/components/Dialog' 11import {Loader} from '#/components/Loader' 12import * as Toast from '#/components/Toast' 13import {Span, Text} from '#/components/Typography' 14import {IS_NATIVE} from '#/env' 15import {useUpdateLiveEventPreferences} from '#/features/liveEvents/preferences' 16import { 17 type LiveEventFeed, 18 type LiveEventFeedMetricContext, 19} from '#/features/liveEvents/types' 20 21export {useDialogControl} from '#/components/Dialog' 22 23export function LiveEventFeedOptionsMenu({ 24 control, 25 feed, 26 metricContext, 27}: { 28 control: Dialog.DialogControlProps 29 feed: LiveEventFeed 30 metricContext: LiveEventFeedMetricContext 31}) { 32 const {_} = useLingui() 33 return ( 34 <Dialog.Outer control={control}> 35 <Dialog.Handle /> 36 <Dialog.ScrollableInner 37 label={_(msg`Configure live event banner`)} 38 style={[web({maxWidth: 400})]}> 39 <Inner control={control} feed={feed} metricContext={metricContext} /> 40 <Dialog.Close /> 41 </Dialog.ScrollableInner> 42 </Dialog.Outer> 43 ) 44} 45 46function Inner({ 47 control, 48 feed, 49 metricContext, 50}: { 51 control: Dialog.DialogControlProps 52 feed: LiveEventFeed 53 metricContext: LiveEventFeedMetricContext 54}) { 55 const {_} = useLingui() 56 const { 57 isPending, 58 mutate: update, 59 error: rawError, 60 variables, 61 } = useUpdateLiveEventPreferences({ 62 feed, 63 metricContext, 64 onUpdateSuccess({undoAction}) { 65 Toast.show( 66 <Toast.Outer> 67 <Toast.Icon /> 68 <Toast.Text> 69 <Trans>Your live event preferences have been updated.</Trans> 70 </Toast.Text> 71 {undoAction && ( 72 <Toast.Action 73 label={_(msg`Undo`)} 74 onPress={() => { 75 if (undoAction) { 76 update(undoAction) 77 } 78 }}> 79 <Trans>Undo</Trans> 80 </Toast.Action> 81 )} 82 </Toast.Outer>, 83 {type: 'success'}, 84 ) 85 86 /* 87 * If there is no `undoAction`, it means that the action was already 88 * undone, and therefore the menu would have been closed prior to the 89 * undo happening. 90 */ 91 if (undoAction) { 92 control.close() 93 } 94 }, 95 }) 96 const cleanError = useCleanError() 97 const error = rawError ? cleanError(rawError) : undefined 98 99 const isHidingFeed = variables?.type === 'hideFeed' && isPending 100 const isHidingAllFeeds = variables?.type === 'toggleHideAllFeeds' && isPending 101 102 return ( 103 <View style={[a.gap_lg]}> 104 <View style={[a.gap_sm]}> 105 <Text style={[a.text_2xl, a.font_semi_bold, a.leading_snug]}> 106 <Trans>Live event options</Trans> 107 </Text> 108 109 <Text style={[a.text_md, a.leading_snug]}> 110 <Trans> 111 Live events appear occasionally when something exciting is 112 happening. If you'd like, you can hide this particular event, or all 113 events for this placement in your app interface. 114 </Trans> 115 </Text> 116 117 <Text style={[a.text_md, a.leading_snug]}> 118 <Trans> 119 If you choose to hide all events, you can always re-enable them from{' '} 120 <Span style={[a.font_semi_bold]}>Settings → Content & Media</Span>. 121 </Trans> 122 </Text> 123 </View> 124 125 <View style={[a.gap_sm]}> 126 <Button 127 label={_(msg`Hide this event`)} 128 size="large" 129 color="primary_subtle" 130 onPress={() => { 131 update({type: 'hideFeed', id: feed.id}) 132 }}> 133 <ButtonText> 134 <Trans>Hide this event</Trans> 135 </ButtonText> 136 {isHidingFeed && <ButtonIcon icon={Loader} />} 137 </Button> 138 <Button 139 label={_(msg`Hide all events`)} 140 size="large" 141 color="secondary" 142 onPress={() => { 143 update({type: 'toggleHideAllFeeds'}) 144 }}> 145 <ButtonText> 146 <Trans>Hide all events</Trans> 147 </ButtonText> 148 {isHidingAllFeeds && <ButtonIcon icon={Loader} />} 149 </Button> 150 {IS_NATIVE && ( 151 <Button 152 label={_(msg`Cancel`)} 153 size="large" 154 color="secondary_inverted" 155 onPress={() => control.close()}> 156 <ButtonText> 157 <Trans>Cancel</Trans> 158 </ButtonText> 159 </Button> 160 )} 161 </View> 162 163 {error && ( 164 <Admonition type="error"> 165 {error.clean || error.raw || _(msg`An unknown error occurred.`)} 166 </Admonition> 167 )} 168 </View> 169 ) 170}