about summary refs log tree commit diff
path: root/src/view/com/modals/ChangeHandle.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/modals/ChangeHandle.tsx')
-rw-r--r--src/view/com/modals/ChangeHandle.tsx614
1 files changed, 0 insertions, 614 deletions
diff --git a/src/view/com/modals/ChangeHandle.tsx b/src/view/com/modals/ChangeHandle.tsx
deleted file mode 100644
index 2181a94aa..000000000
--- a/src/view/com/modals/ChangeHandle.tsx
+++ /dev/null
@@ -1,614 +0,0 @@
-import React, {useState} from 'react'
-import {
-  ActivityIndicator,
-  StyleSheet,
-  TouchableOpacity,
-  View,
-} from 'react-native'
-import {setStringAsync} from 'expo-clipboard'
-import {ComAtprotoServerDescribeServer} from '@atproto/api'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {usePalette} from '#/lib/hooks/usePalette'
-import {cleanError} from '#/lib/strings/errors'
-import {createFullHandle, makeValidHandle} from '#/lib/strings/handles'
-import {s} from '#/lib/styles'
-import {useTheme} from '#/lib/ThemeContext'
-import {logger} from '#/logger'
-import {useModalControls} from '#/state/modals'
-import {useFetchDid, useUpdateHandleMutation} from '#/state/queries/handle'
-import {useServiceQuery} from '#/state/queries/service'
-import {SessionAccount, useAgent, useSession} from '#/state/session'
-import {ErrorMessage} from '../util/error/ErrorMessage'
-import {Button} from '../util/forms/Button'
-import {SelectableBtn} from '../util/forms/SelectableBtn'
-import {Text} from '../util/text/Text'
-import * as Toast from '../util/Toast'
-import {ScrollView, TextInput} from './util'
-
-export const snapPoints = ['100%']
-
-export type Props = {onChanged: () => void}
-
-export function Component(props: Props) {
-  const {currentAccount} = useSession()
-  const agent = useAgent()
-  const {
-    isLoading,
-    data: serviceInfo,
-    error: serviceInfoError,
-  } = useServiceQuery(agent.service.toString())
-
-  return isLoading || !currentAccount ? (
-    <View style={{padding: 18}}>
-      <ActivityIndicator />
-    </View>
-  ) : serviceInfoError || !serviceInfo ? (
-    <ErrorMessage message={cleanError(serviceInfoError)} />
-  ) : (
-    <Inner
-      {...props}
-      currentAccount={currentAccount}
-      serviceInfo={serviceInfo}
-    />
-  )
-}
-
-export function Inner({
-  currentAccount,
-  serviceInfo,
-  onChanged,
-}: Props & {
-  currentAccount: SessionAccount
-  serviceInfo: ComAtprotoServerDescribeServer.OutputSchema
-}) {
-  const {_} = useLingui()
-  const pal = usePalette('default')
-  const {closeModal} = useModalControls()
-  const {mutateAsync: updateHandle, isPending: isUpdateHandlePending} =
-    useUpdateHandleMutation()
-  const agent = useAgent()
-
-  const [error, setError] = useState<string>('')
-
-  const [isCustom, setCustom] = React.useState<boolean>(false)
-  const [handle, setHandle] = React.useState<string>('')
-  const [canSave, setCanSave] = React.useState<boolean>(false)
-
-  const userDomain = serviceInfo.availableUserDomains?.[0]
-
-  // events
-  // =
-  const onPressCancel = React.useCallback(() => {
-    closeModal()
-  }, [closeModal])
-  const onToggleCustom = React.useCallback(() => {
-    // toggle between a provided domain vs a custom one
-    setHandle('')
-    setCanSave(false)
-    setCustom(!isCustom)
-  }, [setCustom, isCustom])
-  const onPressSave = React.useCallback(async () => {
-    if (!userDomain) {
-      logger.error(`ChangeHandle: userDomain is undefined`, {
-        service: serviceInfo,
-      })
-      setError(`The service you've selected has no domains configured.`)
-      return
-    }
-
-    try {
-      const newHandle = isCustom ? handle : createFullHandle(handle, userDomain)
-      logger.debug(`Updating handle to ${newHandle}`)
-      await updateHandle({
-        handle: newHandle,
-      })
-      await agent.resumeSession(agent.session!)
-      closeModal()
-      onChanged()
-    } catch (err: any) {
-      setError(cleanError(err))
-      logger.error('Failed to update handle', {handle, message: err})
-    } finally {
-    }
-  }, [
-    setError,
-    handle,
-    userDomain,
-    isCustom,
-    onChanged,
-    closeModal,
-    updateHandle,
-    serviceInfo,
-    agent,
-  ])
-
-  // rendering
-  // =
-  return (
-    <View style={[s.flex1, pal.view]}>
-      <View style={[styles.title, pal.border]}>
-        <View style={styles.titleLeft}>
-          <TouchableOpacity
-            onPress={onPressCancel}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Cancel change handle`)}
-            accessibilityHint={_(msg`Exits handle change process`)}
-            onAccessibilityEscape={onPressCancel}>
-            <Text type="lg" style={pal.textLight}>
-              <Trans>Cancel</Trans>
-            </Text>
-          </TouchableOpacity>
-        </View>
-        <Text
-          type="2xl-bold"
-          style={[styles.titleMiddle, pal.text]}
-          numberOfLines={1}>
-          <Trans>Change Handle</Trans>
-        </Text>
-        <View style={styles.titleRight}>
-          {isUpdateHandlePending ? (
-            <ActivityIndicator />
-          ) : canSave ? (
-            <TouchableOpacity
-              onPress={onPressSave}
-              accessibilityRole="button"
-              accessibilityLabel={_(msg`Save handle change`)}
-              accessibilityHint={_(msg`Saves handle change to ${handle}`)}>
-              <Text type="2xl-medium" style={pal.link}>
-                <Trans>Save</Trans>
-              </Text>
-            </TouchableOpacity>
-          ) : undefined}
-        </View>
-      </View>
-      <ScrollView style={styles.inner}>
-        {error !== '' && (
-          <View style={styles.errorContainer}>
-            <ErrorMessage message={error} />
-          </View>
-        )}
-
-        {isCustom ? (
-          <CustomHandleForm
-            currentAccount={currentAccount}
-            handle={handle}
-            isProcessing={isUpdateHandlePending}
-            canSave={canSave}
-            onToggleCustom={onToggleCustom}
-            setHandle={setHandle}
-            setCanSave={setCanSave}
-            onPressSave={onPressSave}
-          />
-        ) : (
-          <ProvidedHandleForm
-            handle={handle}
-            userDomain={userDomain}
-            isProcessing={isUpdateHandlePending}
-            onToggleCustom={onToggleCustom}
-            setHandle={setHandle}
-            setCanSave={setCanSave}
-          />
-        )}
-      </ScrollView>
-    </View>
-  )
-}
-
-/**
- * The form for using a domain allocated by the PDS
- */
-function ProvidedHandleForm({
-  userDomain,
-  handle,
-  isProcessing,
-  setHandle,
-  onToggleCustom,
-  setCanSave,
-}: {
-  userDomain: string
-  handle: string
-  isProcessing: boolean
-  setHandle: (v: string) => void
-  onToggleCustom: () => void
-  setCanSave: (v: boolean) => void
-}) {
-  const pal = usePalette('default')
-  const theme = useTheme()
-  const {_} = useLingui()
-
-  // events
-  // =
-  const onChangeHandle = React.useCallback(
-    (v: string) => {
-      const newHandle = makeValidHandle(v)
-      setHandle(newHandle)
-      setCanSave(newHandle.length > 0)
-    },
-    [setHandle, setCanSave],
-  )
-
-  // rendering
-  // =
-  return (
-    <>
-      <View style={[pal.btn, styles.textInputWrapper]}>
-        <FontAwesomeIcon
-          icon="at"
-          style={[pal.textLight, styles.textInputIcon]}
-        />
-        <TextInput
-          testID="setHandleInput"
-          style={[pal.text, styles.textInput]}
-          placeholder={_(msg`e.g. alice`)}
-          placeholderTextColor={pal.colors.textLight}
-          autoCapitalize="none"
-          keyboardAppearance={theme.colorScheme}
-          value={handle}
-          onChangeText={onChangeHandle}
-          editable={!isProcessing}
-          accessible={true}
-          accessibilityLabel={_(msg`Handle`)}
-          accessibilityHint={_(msg`Sets Bluesky username`)}
-        />
-      </View>
-      <Text type="md" style={[pal.textLight, s.pl10, s.pt10]}>
-        <Trans>
-          Your full handle will be{' '}
-          <Text type="md-bold" style={pal.textLight}>
-            @{createFullHandle(handle, userDomain)}
-          </Text>
-        </Trans>
-      </Text>
-      <TouchableOpacity
-        onPress={onToggleCustom}
-        accessibilityRole="button"
-        accessibilityLabel={_(msg`Hosting provider`)}
-        accessibilityHint={_(msg`Opens modal for using custom domain`)}>
-        <Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}>
-          <Trans>I have my own domain</Trans>
-        </Text>
-      </TouchableOpacity>
-    </>
-  )
-}
-
-/**
- * The form for using a custom domain
- */
-function CustomHandleForm({
-  currentAccount,
-  handle,
-  canSave,
-  isProcessing,
-  setHandle,
-  onToggleCustom,
-  onPressSave,
-  setCanSave,
-}: {
-  currentAccount: SessionAccount
-  handle: string
-  canSave: boolean
-  isProcessing: boolean
-  setHandle: (v: string) => void
-  onToggleCustom: () => void
-  onPressSave: () => void
-  setCanSave: (v: boolean) => void
-}) {
-  const pal = usePalette('default')
-  const palSecondary = usePalette('secondary')
-  const palError = usePalette('error')
-  const theme = useTheme()
-  const {_} = useLingui()
-  const [isVerifying, setIsVerifying] = React.useState(false)
-  const [error, setError] = React.useState<string>('')
-  const [isDNSForm, setDNSForm] = React.useState<boolean>(true)
-  const fetchDid = useFetchDid()
-  // events
-  // =
-  const onPressCopy = React.useCallback(() => {
-    setStringAsync(isDNSForm ? `did=${currentAccount.did}` : currentAccount.did)
-    Toast.show(_(msg`Copied to clipboard`), 'clipboard-check')
-  }, [currentAccount, isDNSForm, _])
-  const onChangeHandle = React.useCallback(
-    (v: string) => {
-      setHandle(v)
-      setCanSave(false)
-    },
-    [setHandle, setCanSave],
-  )
-  const onPressVerify = React.useCallback(async () => {
-    if (canSave) {
-      onPressSave()
-    }
-    try {
-      setIsVerifying(true)
-      setError('')
-      const did = await fetchDid(handle)
-      if (did === currentAccount.did) {
-        setCanSave(true)
-      } else {
-        setError(`Incorrect DID returned (got ${did})`)
-      }
-    } catch (err: any) {
-      setError(cleanError(err))
-      logger.error('Failed to verify domain', {handle, error: err})
-    } finally {
-      setIsVerifying(false)
-    }
-  }, [
-    handle,
-    currentAccount,
-    setIsVerifying,
-    setCanSave,
-    setError,
-    canSave,
-    onPressSave,
-    fetchDid,
-  ])
-
-  // rendering
-  // =
-  return (
-    <>
-      <Text type="md" style={[pal.text, s.pb5, s.pl5]} nativeID="customDomain">
-        <Trans>Enter the domain you want to use</Trans>
-      </Text>
-      <View style={[pal.btn, styles.textInputWrapper]}>
-        <FontAwesomeIcon
-          icon="at"
-          style={[pal.textLight, styles.textInputIcon]}
-        />
-        <TextInput
-          testID="setHandleInput"
-          style={[pal.text, styles.textInput]}
-          placeholder={_(msg`e.g. alice.com`)}
-          placeholderTextColor={pal.colors.textLight}
-          autoCapitalize="none"
-          keyboardAppearance={theme.colorScheme}
-          value={handle}
-          onChangeText={onChangeHandle}
-          editable={!isProcessing}
-          accessibilityLabelledBy="customDomain"
-          accessibilityLabel={_(msg`Custom domain`)}
-          accessibilityHint={_(msg`Input your preferred hosting provider`)}
-        />
-      </View>
-      <View style={styles.spacer} />
-
-      <View style={[styles.selectableBtns]}>
-        <SelectableBtn
-          selected={isDNSForm}
-          label={_(msg`DNS Panel`)}
-          left
-          onSelect={() => setDNSForm(true)}
-          accessibilityHint={_(msg`Use the DNS panel`)}
-          style={s.flex1}
-        />
-        <SelectableBtn
-          selected={!isDNSForm}
-          label={_(msg`No DNS Panel`)}
-          right
-          onSelect={() => setDNSForm(false)}
-          accessibilityHint={_(msg`Use a file on your server`)}
-          style={s.flex1}
-        />
-      </View>
-      <View style={styles.spacer} />
-      {isDNSForm ? (
-        <>
-          <Text type="md" style={[pal.text, s.pb5, s.pl5]}>
-            <Trans>Add the following DNS record to your domain:</Trans>
-          </Text>
-          <View style={[styles.dnsTable, pal.btn]}>
-            <Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
-              <Trans>Host:</Trans>
-            </Text>
-            <View style={[styles.dnsValue]}>
-              <Text type="mono" style={[styles.monoText, pal.text]}>
-                _atproto
-              </Text>
-            </View>
-            <Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
-              <Trans>Type:</Trans>
-            </Text>
-            <View style={[styles.dnsValue]}>
-              <Text type="mono" style={[styles.monoText, pal.text]}>
-                TXT
-              </Text>
-            </View>
-            <Text type="md-medium" style={[styles.dnsLabel, pal.text]}>
-              <Trans>Value:</Trans>
-            </Text>
-            <View style={[styles.dnsValue]}>
-              <Text type="mono" style={[styles.monoText, pal.text]}>
-                did={currentAccount.did}
-              </Text>
-            </View>
-          </View>
-          <Text type="md" style={[pal.text, s.pt20, s.pl5]}>
-            <Trans>This should create a domain record at:</Trans>
-          </Text>
-          <Text type="mono" style={[styles.monoText, pal.text, s.pt5, s.pl5]}>
-            _atproto.{handle}
-          </Text>
-        </>
-      ) : (
-        <>
-          <Text type="md" style={[pal.text, s.pb5, s.pl5]}>
-            <Trans>Upload a text file to:</Trans>
-          </Text>
-          <View style={[styles.valueContainer, pal.btn]}>
-            <View style={[styles.dnsValue]}>
-              <Text type="mono" style={[styles.monoText, pal.text]}>
-                https://{handle}/.well-known/atproto-did
-              </Text>
-            </View>
-          </View>
-          <View style={styles.spacer} />
-          <Text type="md" style={[pal.text, s.pb5, s.pl5]}>
-            <Trans>That contains the following:</Trans>
-          </Text>
-          <View style={[styles.valueContainer, pal.btn]}>
-            <View style={[styles.dnsValue]}>
-              <Text type="mono" style={[styles.monoText, pal.text]}>
-                {currentAccount.did}
-              </Text>
-            </View>
-          </View>
-        </>
-      )}
-
-      <View style={styles.spacer} />
-      <Button type="default" style={[s.p20, s.mb10]} onPress={onPressCopy}>
-        <Text type="xl" style={[pal.link, s.textCenter]}>
-          <Trans>
-            Copy {isDNSForm ? _(msg`Domain Value`) : _(msg`File Contents`)}
-          </Trans>
-        </Text>
-      </Button>
-      {canSave === true && (
-        <View style={[styles.message, palSecondary.view]}>
-          <Text type="md-medium" style={palSecondary.text}>
-            <Trans>Domain verified!</Trans>
-          </Text>
-        </View>
-      )}
-      {error ? (
-        <View style={[styles.message, palError.view]}>
-          <Text type="md-medium" style={palError.text}>
-            {error}
-          </Text>
-        </View>
-      ) : null}
-      <Button
-        type="primary"
-        style={[s.p20, isVerifying && styles.dimmed]}
-        onPress={onPressVerify}>
-        {isVerifying ? (
-          <ActivityIndicator color="white" />
-        ) : (
-          <Text type="xl-medium" style={[s.white, s.textCenter]}>
-            {canSave
-              ? _(msg`Update to ${handle}`)
-              : isDNSForm
-              ? _(msg`Verify DNS Record`)
-              : _(msg`Verify Text File`)}
-          </Text>
-        )}
-      </Button>
-      <View style={styles.spacer} />
-      <TouchableOpacity
-        onPress={onToggleCustom}
-        accessibilityLabel={_(msg`Use default provider`)}
-        accessibilityHint={_(msg`Use bsky.social as hosting provider`)}>
-        <Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}>
-          <Trans>Nevermind, create a handle for me</Trans>
-        </Text>
-      </TouchableOpacity>
-    </>
-  )
-}
-
-const styles = StyleSheet.create({
-  inner: {
-    padding: 14,
-  },
-  footer: {
-    padding: 14,
-  },
-  spacer: {
-    height: 20,
-  },
-  dimmed: {
-    opacity: 0.7,
-  },
-
-  selectableBtns: {
-    flexDirection: 'row',
-  },
-
-  title: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    paddingTop: 25,
-    paddingHorizontal: 20,
-    paddingBottom: 15,
-    borderBottomWidth: 1,
-  },
-  titleLeft: {
-    width: 80,
-  },
-  titleRight: {
-    width: 80,
-    flexDirection: 'row',
-    justifyContent: 'flex-end',
-  },
-  titleMiddle: {
-    flex: 1,
-    textAlign: 'center',
-    fontSize: 21,
-  },
-
-  textInputWrapper: {
-    borderRadius: 8,
-    flexDirection: 'row',
-    alignItems: 'center',
-  },
-  textInputIcon: {
-    marginLeft: 12,
-  },
-  textInput: {
-    flex: 1,
-    width: '100%',
-    paddingVertical: 10,
-    paddingHorizontal: 8,
-    fontSize: 17,
-    letterSpacing: 0.25,
-    fontWeight: '400',
-    borderRadius: 10,
-  },
-
-  valueContainer: {
-    borderRadius: 4,
-    paddingVertical: 16,
-  },
-
-  dnsTable: {
-    borderRadius: 4,
-    paddingTop: 2,
-    paddingBottom: 16,
-  },
-  dnsLabel: {
-    paddingHorizontal: 14,
-    paddingTop: 10,
-  },
-  dnsValue: {
-    paddingHorizontal: 14,
-    borderRadius: 4,
-  },
-  monoText: {
-    fontSize: 18,
-    lineHeight: 20,
-  },
-
-  message: {
-    paddingHorizontal: 12,
-    paddingVertical: 10,
-    borderRadius: 8,
-    marginBottom: 10,
-  },
-
-  btn: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    justifyContent: 'center',
-    width: '100%',
-    borderRadius: 32,
-    padding: 10,
-    marginBottom: 10,
-  },
-  errorContainer: {marginBottom: 10},
-})