Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 6982eb4fb4d44105dc8b44e898d452d4e5d32c82 229 lines 6.8 kB view raw
1import React from 'react' 2import {Modal, ScrollView, View} from 'react-native' 3import {SystemBars} from 'react-native-edge-to-edge' 4import {useSafeAreaInsets} from 'react-native-safe-area-context' 5import {msg, plural, Trans} from '@lingui/macro' 6import {useLingui} from '@lingui/react' 7 8import {logger} from '#/logger' 9import {isSignupQueued, useAgent, useSessionApi} from '#/state/session' 10import {pdsAgent} from '#/state/session/agent' 11import {useOnboardingDispatch} from '#/state/shell' 12import {Logo} from '#/view/icons/Logo' 13import {atoms as a, native, useBreakpoints, useTheme, web} from '#/alf' 14import {Button, ButtonIcon, ButtonText} from '#/components/Button' 15import {Loader} from '#/components/Loader' 16import {P, Text} from '#/components/Typography' 17import {IS_IOS, IS_WEB} from '#/env' 18 19const COL_WIDTH = 400 20 21export function SignupQueued() { 22 const {_} = useLingui() 23 const t = useTheme() 24 const insets = useSafeAreaInsets() 25 const {gtMobile} = useBreakpoints() 26 const onboardingDispatch = useOnboardingDispatch() 27 const {logoutCurrentAccount} = useSessionApi() 28 const agent = useAgent() 29 30 const [isProcessing, setProcessing] = React.useState(false) 31 const [estimatedTime, setEstimatedTime] = React.useState<string | undefined>( 32 undefined, 33 ) 34 const [placeInQueue, setPlaceInQueue] = React.useState<number | undefined>( 35 undefined, 36 ) 37 38 const checkStatus = React.useCallback(async () => { 39 setProcessing(true) 40 try { 41 const res = await pdsAgent(agent).com.atproto.temp.checkSignupQueue() 42 if (res.data.activated) { 43 // ready to go, exchange the access token for a usable one and kick off onboarding 44 await agent.sessionManager.refreshSession() 45 if (!isSignupQueued(agent.session?.accessJwt)) { 46 onboardingDispatch({type: 'start'}) 47 } 48 } else { 49 // not ready, update UI 50 setEstimatedTime(msToString(res.data.estimatedTimeMs)) 51 if (typeof res.data.placeInQueue !== 'undefined') { 52 setPlaceInQueue(Math.max(res.data.placeInQueue, 1)) 53 } 54 } 55 } catch (e: any) { 56 logger.error('Failed to check signup queue', {err: e.toString()}) 57 } finally { 58 setProcessing(false) 59 } 60 }, [ 61 setProcessing, 62 setEstimatedTime, 63 setPlaceInQueue, 64 onboardingDispatch, 65 agent, 66 ]) 67 68 React.useEffect(() => { 69 checkStatus() 70 const interval = setInterval(checkStatus, 60e3) 71 return () => clearInterval(interval) 72 }, [checkStatus]) 73 74 const checkBtn = ( 75 <Button 76 variant="solid" 77 color="primary" 78 size="large" 79 label={_(msg`Check my status`)} 80 onPress={checkStatus} 81 disabled={isProcessing}> 82 <ButtonText> 83 <Trans>Check my status</Trans> 84 </ButtonText> 85 {isProcessing && <ButtonIcon icon={Loader} />} 86 </Button> 87 ) 88 89 const logoutBtn = ( 90 <Button 91 variant="ghost" 92 size="large" 93 color="primary" 94 label={_(msg`Sign out`)} 95 onPress={() => logoutCurrentAccount('SignupQueued')}> 96 <ButtonText> 97 <Trans>Sign out</Trans> 98 </ButtonText> 99 </Button> 100 ) 101 102 const webLayout = IS_WEB && gtMobile 103 104 return ( 105 <Modal 106 visible 107 animationType={native('slide')} 108 presentationStyle="formSheet" 109 style={[web(a.util_screen_outer)]}> 110 {IS_IOS && <SystemBars style={{statusBar: 'light'}} />} 111 <ScrollView 112 style={[a.flex_1, t.atoms.bg]} 113 contentContainerStyle={{borderWidth: 0}} 114 bounces={false}> 115 <View 116 style={[ 117 a.flex_row, 118 a.justify_center, 119 gtMobile ? a.pt_4xl : [a.px_xl, a.pt_xl], 120 ]}> 121 <View style={[a.flex_1, {maxWidth: COL_WIDTH}]}> 122 <View 123 style={[a.w_full, a.justify_center, a.align_center, a.my_4xl]}> 124 <Logo width={120} /> 125 </View> 126 127 <Text style={[a.text_4xl, a.font_bold, a.pb_sm]}> 128 <Trans>You're in line</Trans> 129 </Text> 130 <P style={[t.atoms.text_contrast_medium]}> 131 <Trans> 132 There's been a rush of new users to Bluesky! We'll activate your 133 account as soon as we can. 134 </Trans> 135 </P> 136 137 <View 138 style={[ 139 a.rounded_sm, 140 a.px_2xl, 141 a.py_4xl, 142 a.mt_2xl, 143 a.mb_md, 144 a.border, 145 t.atoms.bg_contrast_25, 146 t.atoms.border_contrast_medium, 147 ]}> 148 {typeof placeInQueue === 'number' && ( 149 <Text 150 style={[a.text_5xl, a.text_center, a.font_bold, a.mb_2xl]}> 151 {placeInQueue} 152 </Text> 153 )} 154 <P style={[a.text_center]}> 155 {typeof placeInQueue === 'number' ? ( 156 <Trans>left to go.</Trans> 157 ) : ( 158 <Trans>You are in line.</Trans> 159 )}{' '} 160 {estimatedTime ? ( 161 <Trans> 162 We estimate {estimatedTime} until your account is ready. 163 </Trans> 164 ) : ( 165 <Trans> 166 We will let you know when your account is ready. 167 </Trans> 168 )} 169 </P> 170 </View> 171 172 {webLayout && ( 173 <View 174 style={[ 175 a.w_full, 176 a.flex_row, 177 a.justify_between, 178 a.pt_5xl, 179 {paddingBottom: 200}, 180 ]}> 181 {logoutBtn} 182 {checkBtn} 183 </View> 184 )} 185 </View> 186 </View> 187 </ScrollView> 188 189 {!webLayout && ( 190 <View 191 style={[ 192 a.align_center, 193 t.atoms.bg, 194 gtMobile ? a.px_5xl : a.px_xl, 195 {paddingBottom: Math.max(insets.bottom, a.pb_5xl.paddingBottom)}, 196 ]}> 197 <View style={[a.w_full, a.gap_sm, {maxWidth: COL_WIDTH}]}> 198 {checkBtn} 199 {logoutBtn} 200 </View> 201 </View> 202 )} 203 </Modal> 204 ) 205} 206 207function msToString(ms: number | undefined): string | undefined { 208 if (ms && ms > 0) { 209 const estimatedTimeMins = Math.ceil(ms / 60e3) 210 if (estimatedTimeMins > 59) { 211 const estimatedTimeHrs = Math.round(estimatedTimeMins / 60) 212 if (estimatedTimeHrs > 6) { 213 // dont even bother 214 return undefined 215 } 216 // hours 217 return `${estimatedTimeHrs} ${plural(estimatedTimeHrs, { 218 one: 'hour', 219 other: 'hours', 220 })}` 221 } 222 // minutes 223 return `${estimatedTimeMins} ${plural(estimatedTimeMins, { 224 one: 'minute', 225 other: 'minutes', 226 })}` 227 } 228 return undefined 229}