about summary refs log tree commit diff
path: root/src/screens/Signup/StepInfo
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-03-20 17:25:08 -0500
committerEric Bailey <git@esb.lol>2024-03-20 17:25:08 -0500
commit19fab671a3b11daa73a169b99752a4d2ba9e0166 (patch)
treef173c36f32482bf2a0fd9f91f429d3eb40e79059 /src/screens/Signup/StepInfo
parent58588efceadb030fa83367fb71dbecfce7b828d3 (diff)
downloadvoidsky-19fab671a3b11daa73a169b99752a4d2ba9e0166.tar.zst
Move some things around
Diffstat (limited to 'src/screens/Signup/StepInfo')
-rw-r--r--src/screens/Signup/StepInfo/Policies.tsx97
-rw-r--r--src/screens/Signup/StepInfo/index.tsx146
2 files changed, 243 insertions, 0 deletions
diff --git a/src/screens/Signup/StepInfo/Policies.tsx b/src/screens/Signup/StepInfo/Policies.tsx
new file mode 100644
index 000000000..8a656203f
--- /dev/null
+++ b/src/screens/Signup/StepInfo/Policies.tsx
@@ -0,0 +1,97 @@
+import React from 'react'
+import {View} from 'react-native'
+import {ComAtprotoServerDescribeServer} from '@atproto/api'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {atoms as a, useTheme} from '#/alf'
+import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import {InlineLink} from '#/components/Link'
+import {Text} from '#/components/Typography'
+
+export const Policies = ({
+  serviceDescription,
+  needsGuardian,
+  under13,
+}: {
+  serviceDescription: ComAtprotoServerDescribeServer.OutputSchema
+  needsGuardian: boolean
+  under13: boolean
+}) => {
+  const t = useTheme()
+  const {_} = useLingui()
+
+  if (!serviceDescription) {
+    return <View />
+  }
+
+  const tos = validWebLink(serviceDescription.links?.termsOfService)
+  const pp = validWebLink(serviceDescription.links?.privacyPolicy)
+
+  if (!tos && !pp) {
+    return (
+      <View style={[a.flex_row, a.align_center, a.gap_xs]}>
+        <CircleInfo size="md" fill={t.atoms.text_contrast_low.color} />
+
+        <Text style={[t.atoms.text_contrast_medium]}>
+          <Trans>
+            This service has not provided terms of service or a privacy policy.
+          </Trans>
+        </Text>
+      </View>
+    )
+  }
+
+  const els = []
+  if (tos) {
+    els.push(
+      <InlineLink key="tos" to={tos}>
+        {_(msg`Terms of Service`)}
+      </InlineLink>,
+    )
+  }
+  if (pp) {
+    els.push(
+      <InlineLink key="pp" to={pp}>
+        {_(msg`Privacy Policy`)}
+      </InlineLink>,
+    )
+  }
+  if (els.length === 2) {
+    els.splice(
+      1,
+      0,
+      <Text key="and" style={[t.atoms.text_contrast_medium]}>
+        {' '}
+        and{' '}
+      </Text>,
+    )
+  }
+
+  return (
+    <View style={[a.gap_sm]}>
+      <Text style={[a.leading_snug, t.atoms.text_contrast_medium]}>
+        <Trans>By creating an account you agree to the {els}.</Trans>
+      </Text>
+
+      {under13 ? (
+        <Text style={[a.font_bold, a.leading_snug, t.atoms.text_contrast_high]}>
+          You must be 13 years of age or older to sign up.
+        </Text>
+      ) : needsGuardian ? (
+        <Text style={[a.font_bold, a.leading_snug, t.atoms.text_contrast_high]}>
+          <Trans>
+            If you are not yet an adult according to the laws of your country,
+            your parent or legal guardian must read these Terms on your behalf.
+          </Trans>
+        </Text>
+      ) : undefined}
+    </View>
+  )
+}
+
+function validWebLink(url?: string): string | undefined {
+  return url && (url.startsWith('http://') || url.startsWith('https://'))
+    ? url
+    : undefined
+}
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
new file mode 100644
index 000000000..136592a0b
--- /dev/null
+++ b/src/screens/Signup/StepInfo/index.tsx
@@ -0,0 +1,146 @@
+import React from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {logger} from '#/logger'
+import {ScreenTransition} from '#/screens/Login/ScreenTransition'
+import {is13, is18, useSignupContext} from '#/screens/Signup/state'
+import {Policies} from '#/screens/Signup/StepInfo/Policies'
+import {atoms as a} from '#/alf'
+import * as DateField from '#/components/forms/DateField'
+import {FormError} from '#/components/forms/FormError'
+import {HostingProvider} from '#/components/forms/HostingProvider'
+import * as TextField from '#/components/forms/TextField'
+import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope'
+import {Lock_Stroke2_Corner0_Rounded as Lock} from '#/components/icons/Lock'
+import {Ticket_Stroke2_Corner0_Rounded as Ticket} from '#/components/icons/Ticket'
+import {Loader} from '#/components/Loader'
+
+function sanitizeDate(date: Date): Date {
+  if (!date || date.toString() === 'Invalid Date') {
+    logger.error(`Create account: handled invalid date for birthDate`, {
+      hasDate: !!date,
+    })
+    return new Date()
+  }
+  return date
+}
+
+export function StepInfo() {
+  const {_} = useLingui()
+  const {state, dispatch} = useSignupContext()
+
+  return (
+    <ScreenTransition>
+      <View style={[a.gap_md]}>
+        <FormError error={state.error} />
+        <View>
+          <TextField.Label>
+            <Trans>Hosting provider</Trans>
+          </TextField.Label>
+          <HostingProvider
+            serviceUrl={state.serviceUrl}
+            onSelectServiceUrl={v =>
+              dispatch({type: 'setServiceUrl', value: v})
+            }
+          />
+        </View>
+        {state.isLoading ? (
+          <View style={[a.align_center]}>
+            <Loader size="xl" />
+          </View>
+        ) : state.serviceDescription ? (
+          <>
+            {state.serviceDescription.inviteCodeRequired && (
+              <View>
+                <TextField.Label>
+                  <Trans>Invite code</Trans>
+                </TextField.Label>
+                <TextField.Root>
+                  <TextField.Icon icon={Ticket} />
+                  <TextField.Input
+                    onChangeText={value => {
+                      dispatch({
+                        type: 'setInviteCode',
+                        value: value.trim(),
+                      })
+                    }}
+                    label={_(msg`Required for this provider`)}
+                    defaultValue={state.inviteCode}
+                    autoCapitalize="none"
+                    autoComplete="email"
+                    keyboardType="email-address"
+                  />
+                </TextField.Root>
+              </View>
+            )}
+            <View>
+              <TextField.Label>
+                <Trans>Email</Trans>
+              </TextField.Label>
+              <TextField.Root>
+                <TextField.Icon icon={Envelope} />
+                <TextField.Input
+                  onChangeText={value => {
+                    dispatch({
+                      type: 'setEmail',
+                      value: value.trim(),
+                    })
+                  }}
+                  label={_(msg`Enter your email address`)}
+                  defaultValue={state.email}
+                  autoCapitalize="none"
+                  autoComplete="email"
+                  keyboardType="email-address"
+                />
+              </TextField.Root>
+            </View>
+            <View>
+              <TextField.Label>
+                <Trans>Password</Trans>
+              </TextField.Label>
+              <TextField.Root>
+                <TextField.Icon icon={Lock} />
+                <TextField.Input
+                  onChangeText={value => {
+                    dispatch({
+                      type: 'setPassword',
+                      value,
+                    })
+                  }}
+                  label={_(msg`Choose your password`)}
+                  defaultValue={state.password}
+                  secureTextEntry
+                  autoComplete="new-password"
+                />
+              </TextField.Root>
+            </View>
+            <View>
+              <DateField.Label>
+                <Trans>Your birth date</Trans>
+              </DateField.Label>
+              <DateField.DateField
+                testID="date"
+                value={DateField.utils.toSimpleDateString(state.dateOfBirth)}
+                onChangeDate={date => {
+                  dispatch({
+                    type: 'setDateOfBirth',
+                    value: sanitizeDate(new Date(date)),
+                  })
+                }}
+                label={_(msg`Date of birth`)}
+                accessibilityHint={_(msg`Select your date of birth`)}
+              />
+            </View>
+            <Policies
+              serviceDescription={state.serviceDescription}
+              needsGuardian={!is18(state.dateOfBirth)}
+              under13={!is13(state.dateOfBirth)}
+            />
+          </>
+        ) : undefined}
+      </View>
+    </ScreenTransition>
+  )
+}