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