about summary refs log tree commit diff
path: root/src/screens/SignupQueued.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-06-03 20:10:43 -0500
committerGitHub <noreply@github.com>2024-06-04 02:10:43 +0100
commit3e1f0768916774642516d88254a6cf7a6a82331f (patch)
tree03204ed91e457eef7082c8cf8e1213bb622e5bdb /src/screens/SignupQueued.tsx
parentde93e8de746f3c8a7b1755aaa034043951371ae0 (diff)
downloadvoidsky-3e1f0768916774642516d88254a6cf7a6a82331f.tar.zst
[🙅] Disambiguation of the deactivation (#4267)
* Disambiguation of the deactivation

* Snapshot crackle pop

* Change log context

* [🙅] Add status to session state (#4269)

* Add status to session state

* [🙅] Add new deactivated screen (#4270)

* Add new deactivated screen

* Update copy, handle logout

* Remove icons, adjust padding

* [🙅] Add deactivate account dialog (#4290)

* Deactivate dialog

(cherry picked from commit 33940e2dfe0d710c0665a7f68b198b46f54db4a2)

* Factor out dialog, add to delete modal too

(cherry picked from commit 47d70f6b74e7d2ea7330fd172499fe91ba41062d)

* Update copy, icon

(cherry picked from commit e6efabbe78c3f3d9f0f8fb0a06a6a1c4fbfb70a9)

* Update copy

(cherry picked from commit abb0ce26f6747ab0548f6f12df0dee3c64464852)

* Sizing tweaks

(cherry picked from commit fc716d5716873f0fddef56496fc48af0614b2e55)

* Add a11y label
Diffstat (limited to 'src/screens/SignupQueued.tsx')
-rw-r--r--src/screens/SignupQueued.tsx219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/screens/SignupQueued.tsx b/src/screens/SignupQueued.tsx
new file mode 100644
index 000000000..4e4fedcfa
--- /dev/null
+++ b/src/screens/SignupQueued.tsx
@@ -0,0 +1,219 @@
+import React from 'react'
+import {View} from 'react-native'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
+import {msg, plural, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {logger} from '#/logger'
+import {isWeb} from '#/platform/detection'
+import {isSignupQueued, useAgent, useSessionApi} from '#/state/session'
+import {useOnboardingDispatch} from '#/state/shell'
+import {ScrollView} from '#/view/com/util/Views'
+import {Logo} from '#/view/icons/Logo'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+import {Loader} from '#/components/Loader'
+import {P, Text} from '#/components/Typography'
+
+const COL_WIDTH = 400
+
+export function SignupQueued() {
+  const {_} = useLingui()
+  const t = useTheme()
+  const insets = useSafeAreaInsets()
+  const {gtMobile} = useBreakpoints()
+  const onboardingDispatch = useOnboardingDispatch()
+  const {logout} = useSessionApi()
+  const agent = useAgent()
+
+  const [isProcessing, setProcessing] = React.useState(false)
+  const [estimatedTime, setEstimatedTime] = React.useState<string | undefined>(
+    undefined,
+  )
+  const [placeInQueue, setPlaceInQueue] = React.useState<number | undefined>(
+    undefined,
+  )
+
+  const checkStatus = React.useCallback(async () => {
+    setProcessing(true)
+    try {
+      const res = await agent.com.atproto.temp.checkSignupQueue()
+      if (res.data.activated) {
+        // ready to go, exchange the access token for a usable one and kick off onboarding
+        await agent.refreshSession()
+        if (!isSignupQueued(agent.session?.accessJwt)) {
+          onboardingDispatch({type: 'start'})
+        }
+      } else {
+        // not ready, update UI
+        setEstimatedTime(msToString(res.data.estimatedTimeMs))
+        if (typeof res.data.placeInQueue !== 'undefined') {
+          setPlaceInQueue(Math.max(res.data.placeInQueue, 1))
+        }
+      }
+    } catch (e: any) {
+      logger.error('Failed to check signup queue', {err: e.toString()})
+    } finally {
+      setProcessing(false)
+    }
+  }, [
+    setProcessing,
+    setEstimatedTime,
+    setPlaceInQueue,
+    onboardingDispatch,
+    agent,
+  ])
+
+  React.useEffect(() => {
+    checkStatus()
+    const interval = setInterval(checkStatus, 60e3)
+    return () => clearInterval(interval)
+  }, [checkStatus])
+
+  const checkBtn = (
+    <Button
+      variant="solid"
+      color="primary"
+      size="large"
+      label={_(msg`Check my status`)}
+      onPress={checkStatus}
+      disabled={isProcessing}>
+      <ButtonText>
+        <Trans>Check my status</Trans>
+      </ButtonText>
+      {isProcessing && <ButtonIcon icon={Loader} />}
+    </Button>
+  )
+
+  return (
+    <View
+      aria-modal
+      role="dialog"
+      aria-role="dialog"
+      aria-label={_(msg`You're in line`)}
+      accessibilityLabel={_(msg`You're in line`)}
+      accessibilityHint=""
+      style={[a.absolute, a.inset_0, a.flex_1, t.atoms.bg]}>
+      <ScrollView
+        style={[a.h_full, a.w_full]}
+        contentContainerStyle={{borderWidth: 0}}>
+        <View
+          style={[a.flex_row, a.justify_center, gtMobile ? a.pt_4xl : a.px_xl]}>
+          <View style={[a.flex_1, {maxWidth: COL_WIDTH}]}>
+            <View
+              style={[a.w_full, a.justify_center, a.align_center, a.my_4xl]}>
+              <Logo width={120} />
+            </View>
+
+            <Text style={[a.text_4xl, a.font_bold, a.pb_sm]}>
+              <Trans>You're in line</Trans>
+            </Text>
+            <P style={[t.atoms.text_contrast_medium]}>
+              <Trans>
+                There's been a rush of new users to Bluesky! We'll activate your
+                account as soon as we can.
+              </Trans>
+            </P>
+
+            <View
+              style={[
+                a.rounded_sm,
+                a.px_2xl,
+                a.py_4xl,
+                a.mt_2xl,
+                t.atoms.bg_contrast_50,
+              ]}>
+              {typeof placeInQueue === 'number' && (
+                <Text
+                  style={[a.text_5xl, a.text_center, a.font_bold, a.mb_2xl]}>
+                  {placeInQueue}
+                </Text>
+              )}
+              <P style={[a.text_center]}>
+                {typeof placeInQueue === 'number' ? (
+                  <Trans>left to go.</Trans>
+                ) : (
+                  <Trans>You are in line.</Trans>
+                )}{' '}
+                {estimatedTime ? (
+                  <Trans>
+                    We estimate {estimatedTime} until your account is ready.
+                  </Trans>
+                ) : (
+                  <Trans>
+                    We will let you know when your account is ready.
+                  </Trans>
+                )}
+              </P>
+            </View>
+
+            {isWeb && gtMobile && (
+              <View style={[a.w_full, a.flex_row, a.justify_between, a.pt_5xl]}>
+                <Button
+                  variant="ghost"
+                  size="large"
+                  label={_(msg`Log out`)}
+                  onPress={() => logout('SignupQueued')}>
+                  <ButtonText style={[{color: t.palette.primary_500}]}>
+                    <Trans>Log out</Trans>
+                  </ButtonText>
+                </Button>
+                {checkBtn}
+              </View>
+            )}
+          </View>
+
+          <View style={{height: 200}} />
+        </View>
+      </ScrollView>
+
+      {(!isWeb || !gtMobile) && (
+        <View
+          style={[
+            a.align_center,
+            gtMobile ? a.px_5xl : a.px_xl,
+            {
+              paddingBottom: Math.max(insets.bottom, a.pb_5xl.paddingBottom),
+            },
+          ]}>
+          <View style={[a.w_full, a.gap_sm, {maxWidth: COL_WIDTH}]}>
+            {checkBtn}
+            <Button
+              variant="ghost"
+              size="large"
+              label={_(msg`Log out`)}
+              onPress={() => logout('SignupQueued')}>
+              <ButtonText style={[{color: t.palette.primary_500}]}>
+                <Trans>Log out</Trans>
+              </ButtonText>
+            </Button>
+          </View>
+        </View>
+      )}
+    </View>
+  )
+}
+
+function msToString(ms: number | undefined): string | undefined {
+  if (ms && ms > 0) {
+    const estimatedTimeMins = Math.ceil(ms / 60e3)
+    if (estimatedTimeMins > 59) {
+      const estimatedTimeHrs = Math.round(estimatedTimeMins / 60)
+      if (estimatedTimeHrs > 6) {
+        // dont even bother
+        return undefined
+      }
+      // hours
+      return `${estimatedTimeHrs} ${plural(estimatedTimeHrs, {
+        one: 'hour',
+        other: 'hours',
+      })}`
+    }
+    // minutes
+    return `${estimatedTimeMins} ${plural(estimatedTimeMins, {
+      one: 'minute',
+      other: 'minutes',
+    })}`
+  }
+  return undefined
+}