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