Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
at main 178 lines 5.1 kB view raw
1import {useCallback, useMemo} from 'react' 2import {View} from 'react-native' 3import {msg} from '@lingui/core/macro' 4import {useLingui} from '@lingui/react' 5import {Trans} from '@lingui/react/macro' 6 7import {useOpenLink} from '#/lib/hooks/useOpenLink' 8import {shareUrl} from '#/lib/sharing' 9import {isPossiblyAUrl, splitApexDomain} from '#/lib/strings/url-helpers' 10import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' 11import {Button, ButtonText} from '#/components/Button' 12import * as Dialog from '#/components/Dialog' 13import {Text} from '#/components/Typography' 14import {useGlobalDialogsControlContext} from './Context' 15 16export function LinkWarningDialog() { 17 const {linkWarningDialogControl} = useGlobalDialogsControlContext() 18 19 return ( 20 <Dialog.Outer 21 control={linkWarningDialogControl.control} 22 nativeOptions={{preventExpansion: true}} 23 webOptions={{alignCenter: true}} 24 onClose={linkWarningDialogControl.clear}> 25 <Dialog.Handle /> 26 <LinkWarningDialogInner link={linkWarningDialogControl.value} /> 27 </Dialog.Outer> 28 ) 29} 30 31export function CustomLinkWarningDialog({ 32 control, 33 link, 34}: { 35 control: Dialog.DialogControlProps 36 link?: {href: string; displayText: string; share?: boolean} 37}) { 38 return ( 39 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 40 <Dialog.Handle /> 41 <LinkWarningDialogInner link={link} /> 42 </Dialog.Outer> 43 ) 44} 45 46function LinkWarningDialogInner({ 47 link, 48}: { 49 link?: {href: string; displayText: string; share?: boolean} 50}) { 51 const control = Dialog.useDialogContext() 52 const {_} = useLingui() 53 const t = useTheme() 54 const openLink = useOpenLink() 55 const {gtMobile} = useBreakpoints() 56 57 const potentiallyMisleading = useMemo( 58 () => link && isPossiblyAUrl(link.displayText), 59 [link], 60 ) 61 62 const onPressVisit = useCallback(() => { 63 control.close(() => { 64 if (!link) return 65 if (link.share) { 66 shareUrl(link.href) 67 } else { 68 openLink(link.href, undefined, true) 69 } 70 }) 71 }, [control, link, openLink]) 72 73 const onCancel = useCallback(() => { 74 control.close() 75 }, [control]) 76 77 return ( 78 <Dialog.ScrollableInner 79 style={web({maxWidth: 450})} 80 label={ 81 potentiallyMisleading 82 ? _(msg`Potentially misleading link warning`) 83 : _(msg`Leaving Witchsky`) 84 }> 85 <View style={[a.gap_2xl]}> 86 <View style={[a.gap_sm]}> 87 <Text style={[a.font_bold, a.text_2xl]}> 88 {potentiallyMisleading ? ( 89 <Trans>Potentially misleading link</Trans> 90 ) : ( 91 <Trans>Leaving Witchsky</Trans> 92 )} 93 </Text> 94 <Text style={[t.atoms.text_contrast_high, a.text_md, a.leading_snug]}> 95 <Trans>This link is taking you to the following website:</Trans> 96 </Text> 97 {link && <LinkBox href={link.href} />} 98 {potentiallyMisleading && ( 99 <Text 100 style={[t.atoms.text_contrast_high, a.text_md, a.leading_snug]}> 101 <Trans>Make sure this is where you intend to go!</Trans> 102 </Text> 103 )} 104 </View> 105 <View 106 style={[ 107 a.flex_1, 108 a.gap_sm, 109 gtMobile && [a.flex_row_reverse, a.justify_start], 110 ]}> 111 <Button 112 label={link?.share ? _(msg`Share link`) : _(msg`Visit site`)} 113 accessibilityHint={_(msg`Opens link ${link?.href ?? ''}`)} 114 onPress={onPressVisit} 115 size="large" 116 variant="solid" 117 color={potentiallyMisleading ? 'secondary_inverted' : 'primary'}> 118 <ButtonText> 119 {link?.share ? ( 120 <Trans>Share link</Trans> 121 ) : ( 122 <Trans>Visit site</Trans> 123 )} 124 </ButtonText> 125 </Button> 126 <Button 127 label={_(msg`Go back`)} 128 onPress={onCancel} 129 size="large" 130 variant="ghost" 131 color="secondary"> 132 <ButtonText> 133 <Trans>Go back</Trans> 134 </ButtonText> 135 </Button> 136 </View> 137 </View> 138 <Dialog.Close /> 139 </Dialog.ScrollableInner> 140 ) 141} 142 143function LinkBox({href}: {href: string}) { 144 const t = useTheme() 145 const [scheme, hostname, rest] = useMemo(() => { 146 try { 147 const urlp = new URL(href) 148 const [subdomain, apexdomain] = splitApexDomain(urlp.hostname) 149 return [ 150 urlp.protocol + '//' + subdomain, 151 apexdomain, 152 urlp.pathname.replace(/\/$/, '') + urlp.search + urlp.hash, 153 ] 154 } catch { 155 return ['', href, ''] 156 } 157 }, [href]) 158 return ( 159 <View 160 style={[ 161 t.atoms.bg, 162 t.atoms.border_contrast_medium, 163 a.px_md, 164 {paddingVertical: 10}, 165 a.rounded_sm, 166 a.border, 167 ]}> 168 <Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}> 169 {scheme} 170 <Text 171 style={[a.text_md, a.leading_snug, t.atoms.text, a.font_semi_bold]}> 172 {hostname} 173 </Text> 174 {rest} 175 </Text> 176 </View> 177 ) 178}