Bluesky app fork with some witchin' additions 馃挮
at readme-update 253 lines 8.4 kB view raw
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}