diff --git a/package.json b/package.json
index 837a8d0e..4a3a2a7d 100644
--- a/package.json
+++ b/package.json
@@ -123,7 +123,6 @@
"js-sha256": "^0.9.0",
"jwt-decode": "^4.0.0",
"lande": "^1.0.10",
- "libphonenumber-js": "^1.10.53",
"lodash.chunk": "^4.2.0",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
@@ -137,7 +136,7 @@
"mobx": "^6.6.1",
"mobx-react-lite": "^3.4.0",
"mobx-utils": "^6.0.6",
- "nanoid": "^5.0.2",
+ "nanoid": "^5.0.5",
"normalize-url": "^8.0.0",
"patch-package": "^6.5.1",
"postinstall-postinstall": "^2.1.0",
@@ -164,6 +163,7 @@
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-svg": "14.1.0",
+ "react-native-ui-text-view": "link:./modules/react-native-ui-text-view",
"react-native-url-polyfill": "^1.3.0",
"react-native-uuid": "^2.0.1",
"react-native-version-number": "^0.3.6",
@@ -178,8 +178,7 @@
"tlds": "^1.234.0",
"use-deep-compare": "^1.1.0",
"zeego": "^1.6.2",
- "zod": "^3.20.2",
- "react-native-ui-text-view": "link:./modules/react-native-ui-text-view"
+ "zod": "^3.20.2"
},
"devDependencies": {
"@atproto/dev-env": "^0.2.28",
diff --git a/src/lib/country-codes.ts b/src/lib/country-codes.ts
deleted file mode 100644
index 9c9da84c..00000000
--- a/src/lib/country-codes.ts
+++ /dev/null
@@ -1,256 +0,0 @@
-import {CountryCode} from 'libphonenumber-js'
-
-// ISO 3166-1 alpha-2 codes
-
-export interface CountryCodeMap {
- code2: CountryCode
- name: string
-}
-
-export const COUNTRY_CODES: CountryCodeMap[] = [
- {code2: 'AF', name: 'Afghanistan (+93)'},
- {code2: 'AX', name: 'Åland Islands (+358)'},
- {code2: 'AL', name: 'Albania (+355)'},
- {code2: 'DZ', name: 'Algeria (+213)'},
- {code2: 'AS', name: 'American Samoa (+1)'},
- {code2: 'AD', name: 'Andorra (+376)'},
- {code2: 'AO', name: 'Angola (+244)'},
- {code2: 'AI', name: 'Anguilla (+1)'},
- {code2: 'AG', name: 'Antigua and Barbuda (+1)'},
- {code2: 'AR', name: 'Argentina (+54)'},
- {code2: 'AM', name: 'Armenia (+374)'},
- {code2: 'AW', name: 'Aruba (+297)'},
- {code2: 'AU', name: 'Australia (+61)'},
- {code2: 'AT', name: 'Austria (+43)'},
- {code2: 'AZ', name: 'Azerbaijan (+994)'},
- {code2: 'BS', name: 'Bahamas (+1)'},
- {code2: 'BH', name: 'Bahrain (+973)'},
- {code2: 'BD', name: 'Bangladesh (+880)'},
- {code2: 'BB', name: 'Barbados (+1)'},
- {code2: 'BY', name: 'Belarus (+375)'},
- {code2: 'BE', name: 'Belgium (+32)'},
- {code2: 'BZ', name: 'Belize (+501)'},
- {code2: 'BJ', name: 'Benin (+229)'},
- {code2: 'BM', name: 'Bermuda (+1)'},
- {code2: 'BT', name: 'Bhutan (+975)'},
- {code2: 'BO', name: 'Bolivia (Plurinational State of) (+591)'},
- {code2: 'BQ', name: 'Bonaire, Sint Eustatius and Saba (+599)'},
- {code2: 'BA', name: 'Bosnia and Herzegovina (+387)'},
- {code2: 'BW', name: 'Botswana (+267)'},
- {code2: 'BR', name: 'Brazil (+55)'},
- {code2: 'IO', name: 'British Indian Ocean Territory (+246)'},
- {code2: 'BN', name: 'Brunei Darussalam (+673)'},
- {code2: 'BG', name: 'Bulgaria (+359)'},
- {code2: 'BF', name: 'Burkina Faso (+226)'},
- {code2: 'BI', name: 'Burundi (+257)'},
- {code2: 'CV', name: 'Cabo Verde (+238)'},
- {code2: 'KH', name: 'Cambodia (+855)'},
- {code2: 'CM', name: 'Cameroon (+237)'},
- {code2: 'CA', name: 'Canada (+1)'},
- {code2: 'KY', name: 'Cayman Islands (+1)'},
- {code2: 'CF', name: 'Central African Republic (+236)'},
- {code2: 'TD', name: 'Chad (+235)'},
- {code2: 'CL', name: 'Chile (+56)'},
- {code2: 'CN', name: 'China (+86)'},
- {code2: 'CX', name: 'Christmas Island (+61)'},
- {code2: 'CC', name: 'Cocos (Keeling) Islands (+61)'},
- {code2: 'CO', name: 'Colombia (+57)'},
- {code2: 'KM', name: 'Comoros (+269)'},
- {code2: 'CG', name: 'Congo (+242)'},
- {code2: 'CD', name: 'Congo, Democratic Republic of the (+243)'},
- {code2: 'CK', name: 'Cook Islands (+682)'},
- {code2: 'CR', name: 'Costa Rica (+506)'},
- {code2: 'CI', name: "Côte d'Ivoire (+225)"},
- {code2: 'HR', name: 'Croatia (+385)'},
- {code2: 'CU', name: 'Cuba (+53)'},
- {code2: 'CW', name: 'Curaçao (+599)'},
- {code2: 'CY', name: 'Cyprus (+357)'},
- {code2: 'CZ', name: 'Czechia (+420)'},
- {code2: 'DK', name: 'Denmark (+45)'},
- {code2: 'DJ', name: 'Djibouti (+253)'},
- {code2: 'DM', name: 'Dominica (+1)'},
- {code2: 'DO', name: 'Dominican Republic (+1)'},
- {code2: 'EC', name: 'Ecuador (+593)'},
- {code2: 'EG', name: 'Egypt (+20)'},
- {code2: 'SV', name: 'El Salvador (+503)'},
- {code2: 'GQ', name: 'Equatorial Guinea (+240)'},
- {code2: 'ER', name: 'Eritrea (+291)'},
- {code2: 'EE', name: 'Estonia (+372)'},
- {code2: 'SZ', name: 'Eswatini (+268)'},
- {code2: 'ET', name: 'Ethiopia (+251)'},
- {code2: 'FK', name: 'Falkland Islands (Malvinas) (+500)'},
- {code2: 'FO', name: 'Faroe Islands (+298)'},
- {code2: 'FJ', name: 'Fiji (+679)'},
- {code2: 'FI', name: 'Finland (+358)'},
- {code2: 'FR', name: 'France (+33)'},
- {code2: 'GF', name: 'French Guiana (+594)'},
- {code2: 'PF', name: 'French Polynesia (+689)'},
- {code2: 'GA', name: 'Gabon (+241)'},
- {code2: 'GM', name: 'Gambia (+220)'},
- {code2: 'GE', name: 'Georgia (+995)'},
- {code2: 'DE', name: 'Germany (+49)'},
- {code2: 'GH', name: 'Ghana (+233)'},
- {code2: 'GI', name: 'Gibraltar (+350)'},
- {code2: 'GR', name: 'Greece (+30)'},
- {code2: 'GL', name: 'Greenland (+299)'},
- {code2: 'GD', name: 'Grenada (+1)'},
- {code2: 'GP', name: 'Guadeloupe (+590)'},
- {code2: 'GU', name: 'Guam (+1)'},
- {code2: 'GT', name: 'Guatemala (+502)'},
- {code2: 'GG', name: 'Guernsey (+44)'},
- {code2: 'GN', name: 'Guinea (+224)'},
- {code2: 'GW', name: 'Guinea-Bissau (+245)'},
- {code2: 'GY', name: 'Guyana (+592)'},
- {code2: 'HT', name: 'Haiti (+509)'},
- {code2: 'VA', name: 'Holy See (+39)'},
- {code2: 'HN', name: 'Honduras (+504)'},
- {code2: 'HK', name: 'Hong Kong (+852)'},
- {code2: 'HU', name: 'Hungary (+36)'},
- {code2: 'IS', name: 'Iceland (+354)'},
- {code2: 'IN', name: 'India (+91)'},
- {code2: 'ID', name: 'Indonesia (+62)'},
- {code2: 'IR', name: 'Iran (Islamic Republic of) (+98)'},
- {code2: 'IQ', name: 'Iraq (+964)'},
- {code2: 'IE', name: 'Ireland (+353)'},
- {code2: 'IM', name: 'Isle of Man (+44)'},
- {code2: 'IL', name: 'Israel (+972)'},
- {code2: 'IT', name: 'Italy (+39)'},
- {code2: 'JM', name: 'Jamaica (+1)'},
- {code2: 'JP', name: 'Japan (+81)'},
- {code2: 'JE', name: 'Jersey (+44)'},
- {code2: 'JO', name: 'Jordan (+962)'},
- {code2: 'KZ', name: 'Kazakhstan (+7)'},
- {code2: 'KE', name: 'Kenya (+254)'},
- {code2: 'KI', name: 'Kiribati (+686)'},
- {code2: 'KP', name: "Korea (Democratic People's Republic of) (+850)"},
- {code2: 'KR', name: 'Korea, Republic of (+82)'},
- {code2: 'KW', name: 'Kuwait (+965)'},
- {code2: 'KG', name: 'Kyrgyzstan (+996)'},
- {code2: 'LA', name: "Lao People's Democratic Republic (+856)"},
- {code2: 'LV', name: 'Latvia (+371)'},
- {code2: 'LB', name: 'Lebanon (+961)'},
- {code2: 'LS', name: 'Lesotho (+266)'},
- {code2: 'LR', name: 'Liberia (+231)'},
- {code2: 'LY', name: 'Libya (+218)'},
- {code2: 'LI', name: 'Liechtenstein (+423)'},
- {code2: 'LT', name: 'Lithuania (+370)'},
- {code2: 'LU', name: 'Luxembourg (+352)'},
- {code2: 'MO', name: 'Macao (+853)'},
- {code2: 'MG', name: 'Madagascar (+261)'},
- {code2: 'MW', name: 'Malawi (+265)'},
- {code2: 'MY', name: 'Malaysia (+60)'},
- {code2: 'MV', name: 'Maldives (+960)'},
- {code2: 'ML', name: 'Mali (+223)'},
- {code2: 'MT', name: 'Malta (+356)'},
- {code2: 'MH', name: 'Marshall Islands (+692)'},
- {code2: 'MQ', name: 'Martinique (+596)'},
- {code2: 'MR', name: 'Mauritania (+222)'},
- {code2: 'MU', name: 'Mauritius (+230)'},
- {code2: 'YT', name: 'Mayotte (+262)'},
- {code2: 'MX', name: 'Mexico (+52)'},
- {code2: 'FM', name: 'Micronesia (Federated States of) (+691)'},
- {code2: 'MD', name: 'Moldova, Republic of (+373)'},
- {code2: 'MC', name: 'Monaco (+377)'},
- {code2: 'MN', name: 'Mongolia (+976)'},
- {code2: 'ME', name: 'Montenegro (+382)'},
- {code2: 'MS', name: 'Montserrat (+1)'},
- {code2: 'MA', name: 'Morocco (+212)'},
- {code2: 'MZ', name: 'Mozambique (+258)'},
- {code2: 'MM', name: 'Myanmar (+95)'},
- {code2: 'NA', name: 'Namibia (+264)'},
- {code2: 'NR', name: 'Nauru (+674)'},
- {code2: 'NP', name: 'Nepal (+977)'},
- {code2: 'NL', name: 'Netherlands, Kingdom of the (+31)'},
- {code2: 'NC', name: 'New Caledonia (+687)'},
- {code2: 'NZ', name: 'New Zealand (+64)'},
- {code2: 'NI', name: 'Nicaragua (+505)'},
- {code2: 'NE', name: 'Niger (+227)'},
- {code2: 'NG', name: 'Nigeria (+234)'},
- {code2: 'NU', name: 'Niue (+683)'},
- {code2: 'NF', name: 'Norfolk Island (+672)'},
- {code2: 'MK', name: 'North Macedonia (+389)'},
- {code2: 'MP', name: 'Northern Mariana Islands (+1)'},
- {code2: 'NO', name: 'Norway (+47)'},
- {code2: 'OM', name: 'Oman (+968)'},
- {code2: 'PK', name: 'Pakistan (+92)'},
- {code2: 'PW', name: 'Palau (+680)'},
- {code2: 'PS', name: 'Palestine, State of (+970)'},
- {code2: 'PA', name: 'Panama (+507)'},
- {code2: 'PG', name: 'Papua New Guinea (+675)'},
- {code2: 'PY', name: 'Paraguay (+595)'},
- {code2: 'PE', name: 'Peru (+51)'},
- {code2: 'PH', name: 'Philippines (+63)'},
- {code2: 'PL', name: 'Poland (+48)'},
- {code2: 'PT', name: 'Portugal (+351)'},
- {code2: 'PR', name: 'Puerto Rico (+1)'},
- {code2: 'QA', name: 'Qatar (+974)'},
- {code2: 'RE', name: 'Réunion (+262)'},
- {code2: 'RO', name: 'Romania (+40)'},
- {code2: 'RU', name: 'Russian Federation (+7)'},
- {code2: 'RW', name: 'Rwanda (+250)'},
- {code2: 'BL', name: 'Saint Barthélemy (+590)'},
- {code2: 'SH', name: 'Saint Helena, Ascension and Tristan da Cunha (+290)'},
- {code2: 'KN', name: 'Saint Kitts and Nevis (+1)'},
- {code2: 'LC', name: 'Saint Lucia (+1)'},
- {code2: 'MF', name: 'Saint Martin (French part) (+590)'},
- {code2: 'PM', name: 'Saint Pierre and Miquelon (+508)'},
- {code2: 'VC', name: 'Saint Vincent and the Grenadines (+1)'},
- {code2: 'WS', name: 'Samoa (+685)'},
- {code2: 'SM', name: 'San Marino (+378)'},
- {code2: 'ST', name: 'Sao Tome and Principe (+239)'},
- {code2: 'SA', name: 'Saudi Arabia (+966)'},
- {code2: 'SN', name: 'Senegal (+221)'},
- {code2: 'RS', name: 'Serbia (+381)'},
- {code2: 'SC', name: 'Seychelles (+248)'},
- {code2: 'SL', name: 'Sierra Leone (+232)'},
- {code2: 'SG', name: 'Singapore (+65)'},
- {code2: 'SX', name: 'Sint Maarten (Dutch part) (+1)'},
- {code2: 'SK', name: 'Slovakia (+421)'},
- {code2: 'SI', name: 'Slovenia (+386)'},
- {code2: 'SB', name: 'Solomon Islands (+677)'},
- {code2: 'SO', name: 'Somalia (+252)'},
- {code2: 'ZA', name: 'South Africa (+27)'},
- {code2: 'SS', name: 'South Sudan (+211)'},
- {code2: 'ES', name: 'Spain (+34)'},
- {code2: 'LK', name: 'Sri Lanka (+94)'},
- {code2: 'SD', name: 'Sudan (+249)'},
- {code2: 'SR', name: 'Suriname (+597)'},
- {code2: 'SJ', name: 'Svalbard and Jan Mayen (+47)'},
- {code2: 'SE', name: 'Sweden (+46)'},
- {code2: 'CH', name: 'Switzerland (+41)'},
- {code2: 'SY', name: 'Syrian Arab Republic (+963)'},
- {code2: 'TW', name: 'Taiwan (+886)'},
- {code2: 'TJ', name: 'Tajikistan (+992)'},
- {code2: 'TZ', name: 'Tanzania, United Republic of (+255)'},
- {code2: 'TH', name: 'Thailand (+66)'},
- {code2: 'TL', name: 'Timor-Leste (+670)'},
- {code2: 'TG', name: 'Togo (+228)'},
- {code2: 'TK', name: 'Tokelau (+690)'},
- {code2: 'TO', name: 'Tonga (+676)'},
- {code2: 'TT', name: 'Trinidad and Tobago (+1)'},
- {code2: 'TN', name: 'Tunisia (+216)'},
- {code2: 'TR', name: 'Türkiye (+90)'},
- {code2: 'TM', name: 'Turkmenistan (+993)'},
- {code2: 'TC', name: 'Turks and Caicos Islands (+1)'},
- {code2: 'TV', name: 'Tuvalu (+688)'},
- {code2: 'UG', name: 'Uganda (+256)'},
- {code2: 'UA', name: 'Ukraine (+380)'},
- {code2: 'AE', name: 'United Arab Emirates (+971)'},
- {
- code2: 'GB',
- name: 'United Kingdom of Great Britain and Northern Ireland (+44)',
- },
- {code2: 'US', name: 'United States of America (+1)'},
- {code2: 'UY', name: 'Uruguay (+598)'},
- {code2: 'UZ', name: 'Uzbekistan (+998)'},
- {code2: 'VU', name: 'Vanuatu (+678)'},
- {code2: 'VE', name: 'Venezuela (Bolivarian Republic of) (+58)'},
- {code2: 'VN', name: 'Viet Nam (+84)'},
- {code2: 'VG', name: 'Virgin Islands (British) (+1)'},
- {code2: 'VI', name: 'Virgin Islands (U.S.) (+1)'},
- {code2: 'WF', name: 'Wallis and Futuna (+681)'},
- {code2: 'EH', name: 'Western Sahara (+212)'},
- {code2: 'YE', name: 'Yemen (+967)'},
- {code2: 'ZM', name: 'Zambia (+260)'},
- {code2: 'ZW', name: 'Zimbabwe (+263)'},
-]
diff --git a/src/view/com/auth/create/CaptchaWebView.tsx b/src/view/com/auth/create/CaptchaWebView.tsx
new file mode 100644
index 00000000..b0de8b4a
--- /dev/null
+++ b/src/view/com/auth/create/CaptchaWebView.tsx
@@ -0,0 +1,86 @@
+import React from 'react'
+import {WebView, WebViewNavigation} from 'react-native-webview'
+import {ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes'
+import {StyleSheet} from 'react-native'
+import {CreateAccountState} from 'view/com/auth/create/state'
+
+const ALLOWED_HOSTS = [
+ 'bsky.social',
+ 'bsky.app',
+ 'staging.bsky.app',
+ 'staging.bsky.dev',
+ 'js.hcaptcha.com',
+ 'newassets.hcaptcha.com',
+ 'api2.hcaptcha.com',
+]
+
+export function CaptchaWebView({
+ url,
+ stateParam,
+ uiState,
+ onSuccess,
+ onError,
+}: {
+ url: string
+ stateParam: string
+ uiState?: CreateAccountState
+ onSuccess: (code: string) => void
+ onError: () => void
+}) {
+ const redirectHost = React.useMemo(() => {
+ if (!uiState?.serviceUrl) return 'bsky.app'
+
+ return uiState?.serviceUrl &&
+ new URL(uiState?.serviceUrl).host === 'staging.bsky.dev'
+ ? 'staging.bsky.app'
+ : 'bsky.app'
+ }, [uiState?.serviceUrl])
+
+ const wasSuccessful = React.useRef(false)
+
+ const onShouldStartLoadWithRequest = React.useCallback(
+ (event: ShouldStartLoadRequest) => {
+ const urlp = new URL(event.url)
+ return ALLOWED_HOSTS.includes(urlp.host)
+ },
+ [],
+ )
+
+ const onNavigationStateChange = React.useCallback(
+ (e: WebViewNavigation) => {
+ if (wasSuccessful.current) return
+
+ const urlp = new URL(e.url)
+ if (urlp.host !== redirectHost) return
+
+ const code = urlp.searchParams.get('code')
+ if (urlp.searchParams.get('state') !== stateParam || !code) {
+ onError()
+ return
+ }
+
+ wasSuccessful.current = true
+ onSuccess(code)
+ },
+ [redirectHost, stateParam, onSuccess, onError],
+ )
+
+ return (
+
+ )
+}
+
+const styles = StyleSheet.create({
+ webview: {
+ flex: 1,
+ backgroundColor: 'transparent',
+ borderRadius: 10,
+ },
+})
diff --git a/src/view/com/auth/create/CaptchaWebView.web.tsx b/src/view/com/auth/create/CaptchaWebView.web.tsx
new file mode 100644
index 00000000..7791a58d
--- /dev/null
+++ b/src/view/com/auth/create/CaptchaWebView.web.tsx
@@ -0,0 +1,61 @@
+import React from 'react'
+import {StyleSheet} from 'react-native'
+
+// @ts-ignore web only, we will always redirect to the app on web (CORS)
+const REDIRECT_HOST = new URL(window.location.href).host
+
+export function CaptchaWebView({
+ url,
+ stateParam,
+ onSuccess,
+ onError,
+}: {
+ url: string
+ stateParam: string
+ onSuccess: (code: string) => void
+ onError: () => void
+}) {
+ const onLoad = React.useCallback(() => {
+ // @ts-ignore web
+ const frame: HTMLIFrameElement = document.getElementById(
+ 'captcha-iframe',
+ ) as HTMLIFrameElement
+
+ try {
+ // @ts-ignore web
+ const href = frame?.contentWindow?.location.href
+ if (!href) return
+ const urlp = new URL(href)
+
+ // This shouldn't happen with CORS protections, but for good measure
+ if (urlp.host !== REDIRECT_HOST) return
+
+ const code = urlp.searchParams.get('code')
+ if (urlp.searchParams.get('state') !== stateParam || !code) {
+ onError()
+ return
+ }
+ onSuccess(code)
+ } catch (e) {
+ // We don't need to handle this
+ }
+ }, [stateParam, onSuccess, onError])
+
+ return (
+
+ )
+}
+
+const styles = StyleSheet.create({
+ iframe: {
+ flex: 1,
+ borderWidth: 0,
+ borderRadius: 10,
+ backgroundColor: 'transparent',
+ },
+})
diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx
index 5d452736..8aefffa6 100644
--- a/src/view/com/auth/create/CreateAccount.tsx
+++ b/src/view/com/auth/create/CreateAccount.tsx
@@ -13,33 +13,25 @@ import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {useOnboardingDispatch} from '#/state/shell'
-import {useSessionApi} from '#/state/session'
-import {useCreateAccount, submit} from './state'
+import {useCreateAccount, useSubmitCreateAccount} from './state'
import {useServiceQuery} from '#/state/queries/service'
-import {
- usePreferencesSetBirthDateMutation,
- useSetSaveFeedsMutation,
- DEFAULT_PROD_FEEDS,
-} from '#/state/queries/preferences'
-import {FEEDBACK_FORM_URL, HITSLOP_10, IS_PROD} from '#/lib/constants'
+import {FEEDBACK_FORM_URL, HITSLOP_10} from '#/lib/constants'
import {Step1} from './Step1'
import {Step2} from './Step2'
import {Step3} from './Step3'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {TextLink} from '../../util/Link'
+import {getAgent} from 'state/session'
+import {createFullHandle} from 'lib/strings/handles'
export function CreateAccount({onPressBack}: {onPressBack: () => void}) {
const {screen} = useAnalytics()
const pal = usePalette('default')
const {_} = useLingui()
const [uiState, uiDispatch] = useCreateAccount()
- const onboardingDispatch = useOnboardingDispatch()
- const {createAccount} = useSessionApi()
- const {mutate: setBirthDate} = usePreferencesSetBirthDateMutation()
- const {mutate: setSavedFeeds} = useSetSaveFeedsMutation()
const {isTabletOrDesktop} = useWebMediaQueries()
+ const submit = useSubmitCreateAccount(uiState, uiDispatch)
React.useEffect(() => {
screen('CreateAccount')
@@ -84,33 +76,48 @@ export function CreateAccount({onPressBack}: {onPressBack: () => void}) {
if (!uiState.canNext) {
return
}
- if (uiState.step < 3) {
- uiDispatch({type: 'next'})
- } else {
+
+ if (uiState.step === 2) {
+ uiDispatch({type: 'set-processing', value: true})
try {
- await submit({
- onboardingDispatch,
- createAccount,
- uiState,
- uiDispatch,
- _,
+ const res = await getAgent().resolveHandle({
+ handle: createFullHandle(uiState.handle, uiState.userDomain),
})
- setBirthDate({birthDate: uiState.birthDate})
- if (IS_PROD(uiState.serviceUrl)) {
- setSavedFeeds(DEFAULT_PROD_FEEDS)
+
+ if (res.data.did) {
+ uiDispatch({
+ type: 'set-error',
+ value: _(msg`That handle is already taken.`),
+ })
+ return
}
- } catch {
- // dont need to handle here
+ } catch (e) {
+ // Don't need to handle
+ } finally {
+ uiDispatch({type: 'set-processing', value: false})
+ }
+
+ if (!uiState.isCaptchaRequired) {
+ try {
+ await submit()
+ } catch {
+ // dont need to handle here
+ }
+ // We don't need to go to the next page if there wasn't a captcha required
+ return
}
}
+
+ uiDispatch({type: 'next'})
}, [
- uiState,
+ uiState.canNext,
+ uiState.step,
+ uiState.isCaptchaRequired,
+ uiState.handle,
+ uiState.userDomain,
uiDispatch,
- onboardingDispatch,
- createAccount,
- setBirthDate,
- setSavedFeeds,
_,
+ submit,
])
// rendering
diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx
index a7abbfaa..4c701848 100644
--- a/src/view/com/auth/create/Step1.tsx
+++ b/src/view/com/auth/create/Step1.tsx
@@ -73,6 +73,10 @@ export function Step1({
/>
+ {uiState.error ? (
+
+ ) : undefined}
+
Hosting provider
@@ -259,9 +263,6 @@ export function Step1({
)}
>
)}
- {uiState.error ? (
-
- ) : undefined}
)
}
@@ -269,7 +270,7 @@ export function Step1({
const styles = StyleSheet.create({
error: {
borderRadius: 6,
- marginTop: 10,
+ marginBottom: 10,
},
dateInputButton: {
borderWidth: 1,
diff --git a/src/view/com/auth/create/Step2.tsx b/src/view/com/auth/create/Step2.tsx
index 2e16b13b..87d414bb 100644
--- a/src/view/com/auth/create/Step2.tsx
+++ b/src/view/com/auth/create/Step2.tsx
@@ -1,35 +1,19 @@
import React from 'react'
-import {
- ActivityIndicator,
- StyleSheet,
- TouchableWithoutFeedback,
- View,
-} from 'react-native'
-import RNPickerSelect from 'react-native-picker-select'
-import {
- CreateAccountState,
- CreateAccountDispatch,
- requestVerificationCode,
-} from './state'
+import {StyleSheet, View} from 'react-native'
+import {CreateAccountState, CreateAccountDispatch} from './state'
import {Text} from 'view/com/util/text/Text'
import {StepHeader} from './StepHeader'
import {s} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
import {TextInput} from '../util/TextInput'
-import {Button} from '../../util/forms/Button'
+import {createFullHandle} from 'lib/strings/handles'
+import {usePalette} from 'lib/hooks/usePalette'
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
-import {isAndroid, isWeb} from 'platform/detection'
-import {Trans, msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import parsePhoneNumber from 'libphonenumber-js'
-import {COUNTRY_CODES} from '#/lib/country-codes'
-import {
- FontAwesomeIcon,
- FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import {HITSLOP_10} from '#/lib/constants'
+/** STEP 3: Your user handle
+ * @field User handle
+ */
export function Step2({
uiState,
uiDispatch,
@@ -39,258 +23,34 @@ export function Step2({
}) {
const pal = usePalette('default')
const {_} = useLingui()
- const {isMobile} = useWebMediaQueries()
-
- const onPressRequest = React.useCallback(() => {
- const phoneNumber = parsePhoneNumber(
- uiState.verificationPhone,
- uiState.phoneCountry,
- )
- if (phoneNumber && phoneNumber.isValid()) {
- requestVerificationCode({uiState, uiDispatch, _})
- } else {
- uiDispatch({
- type: 'set-error',
- value: _(
- msg`There's something wrong with this number. Please choose your country and enter your full phone number!`,
- ),
- })
- }
- }, [uiState, uiDispatch, _])
-
- const onPressRetry = React.useCallback(() => {
- uiDispatch({type: 'set-has-requested-verification-code', value: false})
- }, [uiDispatch])
-
- const phoneNumberFormatted = React.useMemo(
- () =>
- uiState.hasRequestedVerificationCode
- ? parsePhoneNumber(
- uiState.verificationPhone,
- uiState.phoneCountry,
- )?.formatInternational()
- : '',
- [
- uiState.hasRequestedVerificationCode,
- uiState.verificationPhone,
- uiState.phoneCountry,
- ],
- )
-
return (
-
-
- {!uiState.hasRequestedVerificationCode ? (
- <>
-
-
- Country
-
-
-
- uiDispatch({type: 'set-phone-country', value})
- }
- items={COUNTRY_CODES.filter(l => Boolean(l.code2)).map(l => ({
- label: l.name,
- value: l.code2,
- key: l.code2,
- }))}
- style={{
- inputAndroid: {
- backgroundColor: pal.view.backgroundColor,
- color: pal.text.color,
- fontSize: 21,
- letterSpacing: 0.5,
- fontWeight: '500',
- paddingHorizontal: 14,
- paddingVertical: 8,
- borderRadius: 4,
- },
- inputIOS: {
- backgroundColor: pal.view.backgroundColor,
- color: pal.text.color,
- fontSize: 14,
- letterSpacing: 0.5,
- fontWeight: '500',
- paddingHorizontal: 14,
- paddingVertical: 8,
- borderWidth: 1,
- borderColor: pal.border.borderColor,
- borderRadius: 4,
- },
- inputWeb: {
- // @ts-ignore web only
- cursor: 'pointer',
- '-moz-appearance': 'none',
- '-webkit-appearance': 'none',
- appearance: 'none',
- outline: 0,
- borderWidth: 1,
- borderColor: pal.border.borderColor,
- backgroundColor: pal.view.backgroundColor,
- color: pal.text.color,
- fontSize: 14,
- letterSpacing: 0.5,
- fontWeight: '500',
- paddingHorizontal: 14,
- paddingVertical: 8,
- borderRadius: 4,
- },
- }}
- accessibilityLabel={_(msg`Select your phone's country`)}
- accessibilityHint=""
- accessibilityLabelledBy="phoneCountry"
- />
-
-
-
-
-
-
-
-
- Phone number
-
-
- uiDispatch({type: 'set-verification-phone', value})
- }
- accessibilityLabel={_(msg`Email`)}
- accessibilityHint={_(
- msg`Input phone number for SMS verification`,
- )}
- accessibilityLabelledBy="phoneNumber"
- keyboardType="phone-pad"
- autoCapitalize="none"
- autoComplete="tel"
- autoCorrect={false}
- autoFocus={true}
- />
-
-
- Please enter a phone number that can receive SMS text messages.
-
-
-
-
-
- {uiState.isProcessing ? (
-
- ) : (
-
- )}
-
- >
- ) : (
- <>
-
-
-
- Verification code{' '}
-
-
-
-
- Retry
-
-
-
-
-
- uiDispatch({type: 'set-verification-code', value})
- }
- accessibilityLabel={_(msg`Email`)}
- accessibilityHint={_(
- msg`Input the verification code we have texted to you`,
- )}
- accessibilityLabelledBy="verificationCode"
- keyboardType="phone-pad"
- autoCapitalize="none"
- autoComplete="one-time-code"
- textContentType="oneTimeCode"
- autoCorrect={false}
- autoFocus={true}
- />
-
-
- Please enter the verification code sent to{' '}
- {phoneNumberFormatted}.
-
-
-
- >
- )}
-
+
{uiState.error ? (
) : undefined}
+
+ uiDispatch({type: 'set-handle', value})}
+ // TODO: Add explicit text label
+ accessibilityLabel={_(msg`User handle`)}
+ accessibilityHint={_(msg`Input your user handle`)}
+ />
+
+ Your full handle will be{' '}
+
+ @{createFullHandle(uiState.handle, uiState.userDomain)}
+
+
+
)
}
@@ -298,10 +58,6 @@ export function Step2({
const styles = StyleSheet.create({
error: {
borderRadius: 6,
- marginTop: 10,
- },
- // @ts-expect-error: Suppressing error due to incomplete `ViewStyle` type definition in react-native-web, missing `cursor` prop as discussed in https://github.com/necolas/react-native-web/issues/832.
- touchable: {
- ...(isWeb && {cursor: 'pointer'}),
+ marginBottom: 10,
},
})
diff --git a/src/view/com/auth/create/Step3.tsx b/src/view/com/auth/create/Step3.tsx
index 3a52abf8..53fdfdde 100644
--- a/src/view/com/auth/create/Step3.tsx
+++ b/src/view/com/auth/create/Step3.tsx
@@ -1,19 +1,23 @@
import React from 'react'
-import {StyleSheet, View} from 'react-native'
-import {CreateAccountState, CreateAccountDispatch} from './state'
-import {Text} from 'view/com/util/text/Text'
+import {ActivityIndicator, StyleSheet, View} from 'react-native'
+import {
+ CreateAccountState,
+ CreateAccountDispatch,
+ useSubmitCreateAccount,
+} from './state'
import {StepHeader} from './StepHeader'
-import {s} from 'lib/styles'
-import {TextInput} from '../util/TextInput'
-import {createFullHandle} from 'lib/strings/handles'
-import {usePalette} from 'lib/hooks/usePalette'
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
-import {msg, Trans} from '@lingui/macro'
+import {isWeb} from 'platform/detection'
+import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-/** STEP 3: Your user handle
- * @field User handle
- */
+import {nanoid} from 'nanoid/non-secure'
+import {CaptchaWebView} from 'view/com/auth/create/CaptchaWebView'
+import {useTheme} from 'lib/ThemeContext'
+import {createFullHandle} from 'lib/strings/handles'
+
+const CAPTCHA_PATH = '/gate/signup'
+
export function Step3({
uiState,
uiDispatch,
@@ -21,33 +25,66 @@ export function Step3({
uiState: CreateAccountState
uiDispatch: CreateAccountDispatch
}) {
- const pal = usePalette('default')
const {_} = useLingui()
+ const theme = useTheme()
+ const submit = useSubmitCreateAccount(uiState, uiDispatch)
+
+ const [completed, setCompleted] = React.useState(false)
+
+ const stateParam = React.useMemo(() => nanoid(15), [])
+ const url = React.useMemo(() => {
+ const newUrl = new URL(uiState.serviceUrl)
+ newUrl.pathname = CAPTCHA_PATH
+ newUrl.searchParams.set(
+ 'handle',
+ createFullHandle(uiState.handle, uiState.userDomain),
+ )
+ newUrl.searchParams.set('state', stateParam)
+ newUrl.searchParams.set('colorScheme', theme.colorScheme)
+
+ console.log(newUrl)
+
+ return newUrl.href
+ }, [
+ uiState.serviceUrl,
+ uiState.handle,
+ uiState.userDomain,
+ stateParam,
+ theme.colorScheme,
+ ])
+
+ const onSuccess = React.useCallback(
+ (code: string) => {
+ setCompleted(true)
+ submit(code)
+ },
+ [submit],
+ )
+
+ const onError = React.useCallback(() => {
+ uiDispatch({
+ type: 'set-error',
+ value: _(msg`Error receiving captcha response.`),
+ })
+ }, [_, uiDispatch])
+
return (
-
-
- uiDispatch({type: 'set-handle', value})}
- // TODO: Add explicit text label
- accessibilityLabel={_(msg`User handle`)}
- accessibilityHint={_(msg`Input your user handle`)}
- />
-
- Your full handle will be{' '}
-
- @{createFullHandle(uiState.handle, uiState.userDomain)}
-
-
+
+
+ {!completed ? (
+
+ ) : (
+
+ )}
+
{uiState.error ? (
) : undefined}
@@ -58,5 +95,20 @@ export function Step3({
const styles = StyleSheet.create({
error: {
borderRadius: 6,
+ marginTop: 10,
+ },
+ // @ts-expect-error: Suppressing error due to incomplete `ViewStyle` type definition in react-native-web, missing `cursor` prop as discussed in https://github.com/necolas/react-native-web/issues/832.
+ touchable: {
+ ...(isWeb && {cursor: 'pointer'}),
+ },
+ container: {
+ minHeight: 500,
+ width: '100%',
+ paddingBottom: 20,
+ overflow: 'hidden',
+ },
+ center: {
+ alignItems: 'center',
+ justifyContent: 'center',
},
})
diff --git a/src/view/com/auth/create/StepHeader.tsx b/src/view/com/auth/create/StepHeader.tsx
index af6bf547..a98b392d 100644
--- a/src/view/com/auth/create/StepHeader.tsx
+++ b/src/view/com/auth/create/StepHeader.tsx
@@ -11,7 +11,7 @@ export function StepHeader({
children,
}: React.PropsWithChildren<{uiState: CreateAccountState; title: string}>) {
const pal = usePalette('default')
- const numSteps = uiState.isPhoneVerificationRequired ? 3 : 2
+ const numSteps = 3
return (
diff --git a/src/view/com/auth/create/state.ts b/src/view/com/auth/create/state.ts
index e8a7cd4e..276eaf92 100644
--- a/src/view/com/auth/create/state.ts
+++ b/src/view/com/auth/create/state.ts
@@ -1,8 +1,7 @@
-import {useReducer} from 'react'
+import {useCallback, useReducer} from 'react'
import {
ComAtprotoServerDescribeServer,
ComAtprotoServerCreateAccount,
- BskyAgent,
} from '@atproto/api'
import {I18nContext, useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
@@ -11,10 +10,14 @@ import {getAge} from 'lib/strings/time'
import {logger} from '#/logger'
import {createFullHandle} from '#/lib/strings/handles'
import {cleanError} from '#/lib/strings/errors'
-import {DispatchContext as OnboardingDispatchContext} from '#/state/shell/onboarding'
-import {ApiContext as SessionApiContext} from '#/state/session'
-import {DEFAULT_SERVICE} from '#/lib/constants'
-import parsePhoneNumber, {CountryCode} from 'libphonenumber-js'
+import {useOnboardingDispatch} from '#/state/shell/onboarding'
+import {useSessionApi} from '#/state/session'
+import {DEFAULT_SERVICE, IS_PROD} from '#/lib/constants'
+import {
+ DEFAULT_PROD_FEEDS,
+ usePreferencesSetBirthDateMutation,
+ useSetSaveFeedsMutation,
+} from 'state/queries/preferences'
export type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema
const DEFAULT_DATE = new Date(Date.now() - 60e3 * 60 * 24 * 365 * 20) // default to 20 years ago
@@ -29,10 +32,6 @@ export type CreateAccountAction =
| {type: 'set-invite-code'; value: string}
| {type: 'set-email'; value: string}
| {type: 'set-password'; value: string}
- | {type: 'set-phone-country'; value: CountryCode}
- | {type: 'set-verification-phone'; value: string}
- | {type: 'set-verification-code'; value: string}
- | {type: 'set-has-requested-verification-code'; value: boolean}
| {type: 'set-handle'; value: string}
| {type: 'set-birth-date'; value: Date}
| {type: 'next'}
@@ -49,10 +48,6 @@ export interface CreateAccountState {
inviteCode: string
email: string
password: string
- phoneCountry: CountryCode
- verificationPhone: string
- verificationCode: string
- hasRequestedVerificationCode: boolean
handle: string
birthDate: Date
@@ -60,13 +55,14 @@ export interface CreateAccountState {
canBack: boolean
canNext: boolean
isInviteCodeRequired: boolean
- isPhoneVerificationRequired: boolean
+ isCaptchaRequired: boolean
}
export type CreateAccountDispatch = (action: CreateAccountAction) => void
export function useCreateAccount() {
const {_} = useLingui()
+
return useReducer(createReducer({_}), {
step: 1,
error: undefined,
@@ -77,144 +73,126 @@ export function useCreateAccount() {
inviteCode: '',
email: '',
password: '',
- phoneCountry: 'US',
- verificationPhone: '',
- verificationCode: '',
- hasRequestedVerificationCode: false,
handle: '',
birthDate: DEFAULT_DATE,
canBack: false,
canNext: false,
isInviteCodeRequired: false,
- isPhoneVerificationRequired: false,
+ isCaptchaRequired: false,
})
}
-export async function requestVerificationCode({
- uiState,
- uiDispatch,
- _,
-}: {
- uiState: CreateAccountState
- uiDispatch: CreateAccountDispatch
- _: I18nContext['_']
-}) {
- const phoneNumber = parsePhoneNumber(
- uiState.verificationPhone,
- uiState.phoneCountry,
- )?.number
- if (!phoneNumber) {
- return
- }
- uiDispatch({type: 'set-error', value: ''})
- uiDispatch({type: 'set-processing', value: true})
- uiDispatch({type: 'set-verification-phone', value: phoneNumber})
- try {
- const agent = new BskyAgent({service: uiState.serviceUrl})
- await agent.com.atproto.temp.requestPhoneVerification({
- phoneNumber,
- })
- uiDispatch({type: 'set-has-requested-verification-code', value: true})
- } catch (e: any) {
- logger.error(
- `Failed to request sms verification code (${e.status} status)`,
- {message: e},
- )
- uiDispatch({type: 'set-error', value: cleanError(e.toString())})
- }
- uiDispatch({type: 'set-processing', value: false})
-}
+export function useSubmitCreateAccount(
+ uiState: CreateAccountState,
+ uiDispatch: CreateAccountDispatch,
+) {
+ const {_} = useLingui()
+ const {createAccount} = useSessionApi()
+ const {mutate: setBirthDate} = usePreferencesSetBirthDateMutation()
+ const {mutate: setSavedFeeds} = useSetSaveFeedsMutation()
+ const onboardingDispatch = useOnboardingDispatch()
-export async function submit({
- createAccount,
- onboardingDispatch,
- uiState,
- uiDispatch,
- _,
-}: {
- createAccount: SessionApiContext['createAccount']
- onboardingDispatch: OnboardingDispatchContext
- uiState: CreateAccountState
- uiDispatch: CreateAccountDispatch
- _: I18nContext['_']
-}) {
- if (!uiState.email) {
- uiDispatch({type: 'set-step', value: 1})
- return uiDispatch({
- type: 'set-error',
- value: _(msg`Please enter your email.`),
- })
- }
- if (!EmailValidator.validate(uiState.email)) {
- uiDispatch({type: 'set-step', value: 1})
- return uiDispatch({
- type: 'set-error',
- value: _(msg`Your email appears to be invalid.`),
- })
- }
- if (!uiState.password) {
- uiDispatch({type: 'set-step', value: 1})
- return uiDispatch({
- type: 'set-error',
- value: _(msg`Please choose your password.`),
- })
- }
- if (
- uiState.isPhoneVerificationRequired &&
- (!uiState.verificationPhone || !uiState.verificationCode)
- ) {
- uiDispatch({type: 'set-step', value: 2})
- return uiDispatch({
- type: 'set-error',
- value: _(msg`Please enter the code you received by SMS.`),
- })
- }
- if (!uiState.handle) {
- uiDispatch({type: 'set-step', value: 3})
- return uiDispatch({
- type: 'set-error',
- value: _(msg`Please choose your handle.`),
- })
- }
- uiDispatch({type: 'set-error', value: ''})
- uiDispatch({type: 'set-processing', value: true})
+ return useCallback(
+ async (verificationCode?: string) => {
+ if (!uiState.email) {
+ uiDispatch({type: 'set-step', value: 1})
+ console.log('no email?')
+ return uiDispatch({
+ type: 'set-error',
+ value: _(msg`Please enter your email.`),
+ })
+ }
+ if (!EmailValidator.validate(uiState.email)) {
+ uiDispatch({type: 'set-step', value: 1})
+ return uiDispatch({
+ type: 'set-error',
+ value: _(msg`Your email appears to be invalid.`),
+ })
+ }
+ if (!uiState.password) {
+ uiDispatch({type: 'set-step', value: 1})
+ return uiDispatch({
+ type: 'set-error',
+ value: _(msg`Please choose your password.`),
+ })
+ }
+ if (!uiState.handle) {
+ uiDispatch({type: 'set-step', value: 2})
+ return uiDispatch({
+ type: 'set-error',
+ value: _(msg`Please choose your handle.`),
+ })
+ }
+ if (uiState.isCaptchaRequired && !verificationCode) {
+ uiDispatch({type: 'set-step', value: 3})
+ return uiDispatch({
+ type: 'set-error',
+ value: _(msg`Please complete the verification captcha.`),
+ })
+ }
+ uiDispatch({type: 'set-error', value: ''})
+ uiDispatch({type: 'set-processing', value: true})
- try {
- onboardingDispatch({type: 'start'}) // start now to avoid flashing the wrong view
- await createAccount({
- service: uiState.serviceUrl,
- email: uiState.email,
- handle: createFullHandle(uiState.handle, uiState.userDomain),
- password: uiState.password,
- inviteCode: uiState.inviteCode.trim(),
- verificationPhone: uiState.verificationPhone.trim(),
- verificationCode: uiState.verificationCode.trim(),
- })
- } catch (e: any) {
- onboardingDispatch({type: 'skip'}) // undo starting the onboard
- let errMsg = e.toString()
- if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
- errMsg = _(
- msg`Invite code not accepted. Check that you input it correctly and try again.`,
- )
- uiDispatch({type: 'set-step', value: 1})
- } else if (e.error === 'InvalidPhoneVerification') {
- uiDispatch({type: 'set-step', value: 2})
- }
+ try {
+ onboardingDispatch({type: 'start'}) // start now to avoid flashing the wrong view
+ await createAccount({
+ service: uiState.serviceUrl,
+ email: uiState.email,
+ handle: createFullHandle(uiState.handle, uiState.userDomain),
+ password: uiState.password,
+ inviteCode: uiState.inviteCode.trim(),
+ verificationCode: uiState.isCaptchaRequired
+ ? verificationCode
+ : undefined,
+ })
+ setBirthDate({birthDate: uiState.birthDate})
+ if (IS_PROD(uiState.serviceUrl)) {
+ setSavedFeeds(DEFAULT_PROD_FEEDS)
+ }
+ } catch (e: any) {
+ onboardingDispatch({type: 'skip'}) // undo starting the onboard
+ let errMsg = e.toString()
+ if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
+ errMsg = _(
+ msg`Invite code not accepted. Check that you input it correctly and try again.`,
+ )
+ uiDispatch({type: 'set-step', value: 1})
+ }
- if ([400, 429].includes(e.status)) {
- logger.warn('Failed to create account', {message: e})
- } else {
- logger.error(`Failed to create account (${e.status} status)`, {
- message: e,
- })
- }
+ if ([400, 429].includes(e.status)) {
+ logger.warn('Failed to create account', {message: e})
+ } else {
+ logger.error(`Failed to create account (${e.status} status)`, {
+ message: e,
+ })
+ }
- uiDispatch({type: 'set-processing', value: false})
- uiDispatch({type: 'set-error', value: cleanError(errMsg)})
- throw e
- }
+ const error = cleanError(errMsg)
+ const isHandleError = error.toLowerCase().includes('handle')
+
+ uiDispatch({type: 'set-processing', value: false})
+ uiDispatch({type: 'set-error', value: cleanError(errMsg)})
+ uiDispatch({type: 'set-step', value: isHandleError ? 2 : 1})
+ }
+ },
+ [
+ uiState.email,
+ uiState.password,
+ uiState.handle,
+ uiState.isCaptchaRequired,
+ uiState.serviceUrl,
+ uiState.userDomain,
+ uiState.inviteCode,
+ uiState.birthDate,
+ uiDispatch,
+ _,
+ onboardingDispatch,
+ createAccount,
+ setBirthDate,
+ setSavedFeeds,
+ ],
+ )
}
export function is13(state: CreateAccountState) {
@@ -269,22 +247,6 @@ function createReducer({_}: {_: I18nContext['_']}) {
case 'set-password': {
return compute({...state, password: action.value})
}
- case 'set-phone-country': {
- return compute({...state, phoneCountry: action.value})
- }
- case 'set-verification-phone': {
- return compute({
- ...state,
- verificationPhone: action.value,
- hasRequestedVerificationCode: false,
- })
- }
- case 'set-verification-code': {
- return compute({...state, verificationCode: action.value.trim()})
- }
- case 'set-has-requested-verification-code': {
- return compute({...state, hasRequestedVerificationCode: action.value})
- }
case 'set-handle': {
return compute({...state, handle: action.value})
}
@@ -302,18 +264,10 @@ function createReducer({_}: {_: I18nContext['_']}) {
})
}
}
- let increment = 1
- if (state.step === 1 && !state.isPhoneVerificationRequired) {
- increment = 2
- }
- return compute({...state, error: '', step: state.step + increment})
+ return compute({...state, error: '', step: state.step + 1})
}
case 'back': {
- let decrement = 1
- if (state.step === 3 && !state.isPhoneVerificationRequired) {
- decrement = 2
- }
- return compute({...state, error: '', step: state.step - decrement})
+ return compute({...state, error: '', step: state.step - 1})
}
}
}
@@ -328,23 +282,16 @@ function compute(state: CreateAccountState): CreateAccountState {
!!state.email &&
!!state.password
} else if (state.step === 2) {
- canNext =
- !state.isPhoneVerificationRequired ||
- (!!state.verificationPhone &&
- isValidVerificationCode(state.verificationCode))
- } else if (state.step === 3) {
canNext = !!state.handle
+ } else if (state.step === 3) {
+ // Step 3 will automatically redirect as soon as the captcha completes
+ canNext = false
}
return {
...state,
canBack: state.step > 1,
canNext,
isInviteCodeRequired: !!state.serviceDescription?.inviteCodeRequired,
- isPhoneVerificationRequired:
- !!state.serviceDescription?.phoneVerificationRequired,
+ isCaptchaRequired: !!state.serviceDescription?.phoneVerificationRequired,
}
}
-
-function isValidVerificationCode(str: string): boolean {
- return /[0-9]{6}/.test(str)
-}
diff --git a/yarn.lock b/yarn.lock
index d26d0987..a85ea79b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15158,11 +15158,6 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
-libphonenumber-js@^1.10.53:
- version "1.10.53"
- resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.53.tgz#8dbfe1355ef1a3d8e13b8d92849f7db7ebddc98f"
- integrity sha512-sDTnnqlWK4vH4AlDQuswz3n4Hx7bIQWTpIcScJX+Sp7St3LXHmfiax/ZFfyYxHmkdCvydOLSuvtAO/XpXiSySw==
-
lie@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
@@ -16157,10 +16152,10 @@ nanoid@^3.1.23, nanoid@^3.3.1, nanoid@^3.3.6:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
-nanoid@^5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.2.tgz#97588ebc70166d0feaf73ccd2799bb4ceaebf692"
- integrity sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==
+nanoid@^5.0.5:
+ version "5.0.5"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.5.tgz#5112efb5c0caf4fc80680d66d303c65233a79fdd"
+ integrity sha512-/Veqm+QKsyMY3kqi4faWplnY1u+VuKO3dD2binyPIybP31DRO29bPF+1mszgLnrR2KqSLceFLBNw0zmvDzN1QQ==
napi-build-utils@^1.0.1:
version "1.0.2"