about summary refs log tree commit diff
path: root/src/state/models/ui/create-account.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/models/ui/create-account.ts')
-rw-r--r--src/state/models/ui/create-account.ts192
1 files changed, 192 insertions, 0 deletions
diff --git a/src/state/models/ui/create-account.ts b/src/state/models/ui/create-account.ts
new file mode 100644
index 000000000..a212fe05e
--- /dev/null
+++ b/src/state/models/ui/create-account.ts
@@ -0,0 +1,192 @@
+import {makeAutoObservable} from 'mobx'
+import {RootStoreModel} from '../root-store'
+import {ServiceDescription} from '../session'
+import {DEFAULT_SERVICE} from 'state/index'
+import {ComAtprotoAccountCreate} from '@atproto/api'
+import * as EmailValidator from 'email-validator'
+import {createFullHandle} from 'lib/strings/handles'
+import {cleanError} from 'lib/strings/errors'
+
+export class CreateAccountModel {
+  step: number = 1
+  isProcessing = false
+  isFetchingServiceDescription = false
+  didServiceDescriptionFetchFail = false
+  error = ''
+
+  serviceUrl = DEFAULT_SERVICE
+  serviceDescription: ServiceDescription | undefined = undefined
+  userDomain = ''
+  inviteCode = ''
+  email = ''
+  password = ''
+  handle = ''
+  is13 = false
+
+  constructor(public rootStore: RootStoreModel) {
+    makeAutoObservable(this, {}, {autoBind: true})
+  }
+
+  // form state controls
+  // =
+
+  next() {
+    this.error = ''
+    this.step++
+  }
+
+  back() {
+    this.error = ''
+    this.step--
+  }
+
+  setStep(v: number) {
+    this.step = v
+  }
+
+  async fetchServiceDescription() {
+    this.setError('')
+    this.setIsFetchingServiceDescription(true)
+    this.setDidServiceDescriptionFetchFail(false)
+    this.setServiceDescription(undefined)
+    if (!this.serviceUrl) {
+      return
+    }
+    try {
+      const desc = await this.rootStore.session.describeService(this.serviceUrl)
+      this.setServiceDescription(desc)
+      this.setUserDomain(desc.availableUserDomains[0])
+    } catch (err: any) {
+      this.rootStore.log.warn(
+        `Failed to fetch service description for ${this.serviceUrl}`,
+        err,
+      )
+      this.setError(
+        'Unable to contact your service. Please check your Internet connection.',
+      )
+      this.setDidServiceDescriptionFetchFail(true)
+    } finally {
+      this.setIsFetchingServiceDescription(false)
+    }
+  }
+
+  async submit() {
+    if (!this.email) {
+      this.setStep(2)
+      return this.setError('Please enter your email.')
+    }
+    if (!EmailValidator.validate(this.email)) {
+      this.setStep(2)
+      return this.setError('Your email appears to be invalid.')
+    }
+    if (!this.password) {
+      this.setStep(2)
+      return this.setError('Please choose your password.')
+    }
+    if (!this.handle) {
+      this.setStep(3)
+      return this.setError('Please choose your handle.')
+    }
+    this.setError('')
+    this.setIsProcessing(true)
+    try {
+      await this.rootStore.session.createAccount({
+        service: this.serviceUrl,
+        email: this.email,
+        handle: createFullHandle(this.handle, this.userDomain),
+        password: this.password,
+        inviteCode: this.inviteCode,
+      })
+    } catch (e: any) {
+      let errMsg = e.toString()
+      if (e instanceof ComAtprotoAccountCreate.InvalidInviteCodeError) {
+        errMsg =
+          'Invite code not accepted. Check that you input it correctly and try again.'
+      }
+      this.rootStore.log.error('Failed to create account', e)
+      this.setIsProcessing(false)
+      this.setError(cleanError(errMsg))
+      throw e
+    }
+  }
+
+  // form state accessors
+  // =
+
+  get canBack() {
+    return this.step > 1
+  }
+
+  get canNext() {
+    if (this.step === 1) {
+      return !!this.serviceDescription
+    } else if (this.step === 2) {
+      return (
+        (!this.isInviteCodeRequired || this.inviteCode) &&
+        !!this.email &&
+        !!this.password &&
+        this.is13
+      )
+    }
+    return !!this.handle
+  }
+
+  get isServiceDescribed() {
+    return !!this.serviceDescription
+  }
+
+  get isInviteCodeRequired() {
+    return this.serviceDescription?.inviteCodeRequired
+  }
+
+  // setters
+  // =
+
+  setIsProcessing(v: boolean) {
+    this.isProcessing = v
+  }
+
+  setIsFetchingServiceDescription(v: boolean) {
+    this.isFetchingServiceDescription = v
+  }
+
+  setDidServiceDescriptionFetchFail(v: boolean) {
+    this.didServiceDescriptionFetchFail = v
+  }
+
+  setError(v: string) {
+    this.error = v
+  }
+
+  setServiceUrl(v: string) {
+    this.serviceUrl = v
+  }
+
+  setServiceDescription(v: ServiceDescription | undefined) {
+    this.serviceDescription = v
+  }
+
+  setUserDomain(v: string) {
+    this.userDomain = v
+  }
+
+  setInviteCode(v: string) {
+    this.inviteCode = v
+  }
+
+  setEmail(v: string) {
+    this.email = v
+  }
+
+  setPassword(v: string) {
+    this.password = v
+  }
+
+  setHandle(v: string) {
+    this.handle = v
+  }
+
+  setIs13(v: boolean) {
+    this.is13 = v
+  }
+}