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