forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useEffect, useState} from 'react'
2import {Pressable, View} from 'react-native'
3import {ImageBackground} from 'expo-image'
4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6import {FocusGuards, FocusScope} from 'radix-ui/internal'
7
8import {useLoggedOutViewControls} from '#/state/shell/logged-out'
9import {Logo} from '#/view/icons/Logo'
10import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf'
11import {Button, ButtonText} from '#/components/Button'
12import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
13import {Text} from '#/components/Typography'
14import {useAnalytics} from '#/analytics'
15
16const welcomeModalBg = require('../../assets/images/welcome-modal-bg.webp')
17
18interface WelcomeModalProps {
19 control: {
20 isOpen: boolean
21 open: () => void
22 close: () => void
23 }
24}
25
26export function WelcomeModal({control}: WelcomeModalProps) {
27 const {_} = useLingui()
28 const ax = useAnalytics()
29 const {requestSwitchToAccount} = useLoggedOutViewControls()
30 const {gtMobile} = useBreakpoints()
31 const [isExiting, setIsExiting] = useState(false)
32 const [signInLinkHovered, setSignInLinkHovered] = useState(false)
33 const t = useTheme()
34
35 const fadeOutAndClose = (callback?: () => void) => {
36 setIsExiting(true)
37 setTimeout(() => {
38 control.close()
39 if (callback) callback()
40 }, 150)
41 }
42
43 useEffect(() => {
44 if (control.isOpen) {
45 ax.metric('welcomeModal:presented', {})
46 }
47 // eslint-disable-next-line react-hooks/exhaustive-deps
48 }, [control.isOpen])
49
50 const onPressCreateAccount = () => {
51 ax.metric('welcomeModal:signupClicked', {})
52 control.close()
53 requestSwitchToAccount({requestedAccount: 'new'})
54 }
55
56 const onPressExplore = () => {
57 ax.metric('welcomeModal:exploreClicked', {})
58 fadeOutAndClose()
59 }
60
61 const onPressSignIn = () => {
62 ax.metric('welcomeModal:signinClicked', {})
63 control.close()
64 requestSwitchToAccount({requestedAccount: 'existing'})
65 }
66
67 FocusGuards.useFocusGuards()
68
69 return (
70 <View
71 role="dialog"
72 aria-modal
73 style={[
74 a.fixed,
75 a.inset_0,
76 a.justify_center,
77 a.align_center,
78 {zIndex: 9999, backgroundColor: 'rgba(0,0,0,0.2)'},
79 web({backdropFilter: 'blur(15px)'}),
80 isExiting ? a.fade_out : a.fade_in,
81 ]}>
82 <FocusScope.FocusScope asChild loop trapped>
83 <View
84 style={flatten([
85 {
86 maxWidth: 800,
87 maxHeight: 600,
88 width: '90%',
89 height: '90%',
90 backgroundColor: '#c0cdec',
91 },
92 a.rounded_lg,
93 a.overflow_hidden,
94 a.zoom_in,
95 ])}>
96 <ImageBackground
97 source={welcomeModalBg}
98 style={[a.flex_1, a.justify_center]}
99 contentFit="cover">
100 <View style={[a.gap_2xl, a.align_center, a.p_4xl]}>
101 <View
102 style={[
103 a.flex_row,
104 a.align_center,
105 a.justify_center,
106 a.w_full,
107 a.p_0,
108 ]}>
109 <View style={[a.flex_row, a.align_center, a.gap_xs]}>
110 <Logo width={26} />
111 <Text
112 style={[
113 a.text_2xl,
114 a.font_semi_bold,
115 a.user_select_none,
116 {color: 'rgb(42, 40, 40)', letterSpacing: -0.5},
117 ]}>
118 Witchsky
119 </Text>
120 </View>
121 </View>
122 <View
123 style={[
124 a.gap_sm,
125 a.align_center,
126 a.pt_5xl,
127 a.pb_3xl,
128 a.mt_2xl,
129 ]}>
130 <Text
131 style={[
132 gtMobile ? a.text_4xl : a.text_3xl,
133 a.font_semi_bold,
134 a.text_center,
135 {color: 'rgb(55, 45, 45)'},
136 web({
137 backgroundImage:
138 'linear-gradient(180deg, rgb(87, 77, 77) 0%, rgb(95, 68, 68) 83.65%, rgba(107, 68, 68, 0.47) 100%)',
139 backgroundClip: 'text',
140 WebkitBackgroundClip: 'text',
141 WebkitTextFillColor: 'transparent',
142 color: 'transparent',
143 lineHeight: 1.2,
144 letterSpacing: -0.5,
145 }),
146 ]}>
147 <Trans>Real talk.</Trans>
148 {'\n'}
149 <Trans>Real creatures.</Trans>
150 {'\n'}
151 <Trans>Social media if it was good.</Trans>
152 </Text>
153 </View>
154 <View style={[a.gap_md, a.align_center]}>
155 <View>
156 <Button
157 onPress={onPressCreateAccount}
158 label={_(msg`Create account`)}
159 size="large"
160 color="primary"
161 style={{
162 width: 200,
163 backgroundColor: t.palette.primary_500,
164 }}>
165 <ButtonText>
166 <Trans>Create account</Trans>
167 </ButtonText>
168 </Button>
169 <Button
170 onPress={onPressExplore}
171 label={_(msg`Explore the app`)}
172 size="large"
173 color="primary"
174 variant="ghost"
175 style={[a.bg_transparent, {width: 200}]}
176 hoverStyle={[a.bg_transparent]}>
177 {({hovered}) => (
178 <ButtonText
179 style={[
180 hovered && [a.underline],
181 {color: t.palette.primary_500},
182 ]}>
183 <Trans>Explore the app</Trans>
184 </ButtonText>
185 )}
186 </Button>
187 </View>
188 <View style={[a.align_center, {minWidth: 200}]}>
189 <Text
190 style={[
191 a.text_md,
192 a.text_center,
193 {color: 'rgb(58, 50, 50)', lineHeight: 24},
194 ]}>
195 <Trans>Already have an account?</Trans>{' '}
196 <Pressable
197 onPointerEnter={() => setSignInLinkHovered(true)}
198 onPointerLeave={() => setSignInLinkHovered(false)}
199 accessibilityRole="button"
200 accessibilityLabel={_(msg`Sign in`)}
201 accessibilityHint="">
202 <Text
203 style={[
204 a.font_medium,
205 {
206 color: t.palette.primary_500,
207 fontSize: undefined,
208 },
209 signInLinkHovered && a.underline,
210 ]}
211 onPress={onPressSignIn}>
212 <Trans>Sign in</Trans>
213 </Text>
214 </Pressable>
215 </Text>
216 </View>
217 </View>
218 </View>
219 <Button
220 label={_(msg`Close welcome modal`)}
221 style={[
222 a.absolute,
223 {
224 top: 8,
225 right: 8,
226 },
227 a.bg_transparent,
228 ]}
229 hoverStyle={[a.bg_transparent]}
230 onPress={() => {
231 ax.metric('welcomeModal:dismissed', {})
232 fadeOutAndClose()
233 }}
234 color="secondary"
235 size="small"
236 variant="ghost"
237 shape="round">
238 {({hovered, pressed, focused}) => (
239 <XIcon
240 size="md"
241 style={{
242 color: 'rgb(77, 47, 47)',
243 opacity: hovered || pressed || focused ? 1 : 0.7,
244 }}
245 />
246 )}
247 </Button>
248 </ImageBackground>
249 </View>
250 </FocusScope.FocusScope>
251 </View>
252 )
253}