diff --git a/shared/settings/account/add-modals.tsx b/shared/settings/account/add-modals.tsx
index db284fefe26e..c8c544a70fdc 100644
--- a/shared/settings/account/add-modals.tsx
+++ b/shared/settings/account/add-modals.tsx
@@ -274,7 +274,6 @@ export const VerifyPhone = () => {
diff --git a/shared/signup/common.tsx b/shared/signup/common.tsx
index 0bfd0b91b3e5..2c3fb5a1a84a 100644
--- a/shared/signup/common.tsx
+++ b/shared/signup/common.tsx
@@ -62,6 +62,43 @@ type HeaderProps = {
onRightAction?: () => void
}
+const HeaderInfoIcon = (props: Pick) => {
+ if (!props.showInfoIcon && !props.showInfoIconRow) {
+ return null
+ }
+
+ return (
+
+
+
+ )
+}
+
+const HeaderRightAction = (props: Pick) => {
+ if (props.rightActionComponent) {
+ return (
+
+ {props.rightActionComponent}
+
+ )
+ }
+
+ if (!props.onRightAction || !props.rightActionLabel) {
+ return null
+ }
+
+ return (
+
+ )
+}
+
// Only used on desktop
const Header = (props: HeaderProps) => (
(
fullWidth={true}
style={Kb.Styles.collapseStyles([styles.headerContainer, props.style])}
>
- {(props.showInfoIcon || props.showInfoIconRow) && (
-
-
-
- )}
+
{props.onBack && (
@@ -95,21 +132,11 @@ const Header = (props: HeaderProps) => (
)}
{props.titleComponent || {props.title}}
- {props.onRightAction && !!props.rightActionLabel && (
-
- )}
- {props.rightActionComponent && (
-
- {props.rightActionComponent}
-
- )}
+
)
@@ -140,89 +167,96 @@ type SignupScreenProps = {
rightActionComponent?: React.ReactNode
rightActionLabel?: string
onRightAction?: () => void
+ showHeaderInfoIcon?: boolean
+ showHeaderInfoIconRow?: boolean
showHeaderInfoicon?: boolean
showHeaderInfoiconRow?: boolean
}
+const SignupButtons = (props: {buttons?: Array}) =>
+ !props.buttons ? null : (
+
+ {props.buttons.map(button =>
+ button.waitingKey !== undefined ? (
+
+ ) : (
+
+ )
+ )}
+
+ )
+
// Screens with header + body bg color (i.e. all but join-or-login)
-export const SignupScreen = (props: SignupScreenProps) => (
-
- {!Kb.Styles.isMobile && (
-
- )}
- {Kb.Styles.isMobile && props.header}
+export const SignupScreen = (props: SignupScreenProps) => {
+ const showHeaderInfoIcon = props.showHeaderInfoIcon ?? props.showHeaderInfoicon
+ const showHeaderInfoIconRow = props.showHeaderInfoIconRow ?? props.showHeaderInfoiconRow
+
+ return (
+ {!Kb.Styles.isMobile && (
+
+ )}
+ {Kb.Styles.isMobile && props.header}
- {props.children}
-
- {!!props.footer && (
-
- {props.footer}
-
- )}
- {/* Banners after children so they go on top */}
- {!!props.banners && }
- {!!props.buttons && (
-
- {props.buttons.map(b =>
- b.waitingKey !== undefined ? (
-
- ) : (
-
- )
- )}
-
- )}
+ {props.children}
+
+ {!!props.footer && (
+
+ {props.footer}
+
+ )}
+ {!!props.banners && }
+
+
-
-)
+ )
+}
export const errorBanner = (error: string) =>
error.trim() ? (
diff --git a/shared/signup/device-name.tsx b/shared/signup/device-name.tsx
index fc55f9791f42..20a5b1183574 100644
--- a/shared/signup/device-name.tsx
+++ b/shared/signup/device-name.tsx
@@ -6,21 +6,25 @@ import * as Provision from '@/stores/provision'
import {useSignupState} from '@/stores/signup'
const ConnectedEnterDevicename = () => {
- const error = useSignupState(s => s.devicenameError)
- const initialDevicename = useSignupState(s => s.devicename)
+ const {checkDeviceName, error, goBackAndClearErrors, initialDevicename} = useSignupState(
+ C.useShallow(s => ({
+ checkDeviceName: s.dispatch.checkDeviceName,
+ error: s.devicenameError,
+ goBackAndClearErrors: s.dispatch.goBackAndClearErrors,
+ initialDevicename: s.devicename,
+ }))
+ )
const waiting = C.Waiting.useAnyWaiting(C.waitingKeyProvision)
- const goBackAndClearErrors = useSignupState(s => s.dispatch.goBackAndClearErrors)
- const checkDeviceName = useSignupState(s => s.dispatch.checkDeviceName)
- const onBack = goBackAndClearErrors
- const onContinue = checkDeviceName
- const props = {
- error,
- initialDevicename,
- onBack,
- onContinue,
- waiting,
- }
- return
+
+ return (
+
+ )
}
export default ConnectedEnterDevicename
@@ -53,12 +57,18 @@ const EnterDevicename = (props: Props) => {
!Provision.goodDeviceRE.test(cleanDeviceName) ||
Provision.badDeviceRE.test(cleanDeviceName)
const showDisabled = disabled && !!cleanDeviceName && readyToShowError
- const _setDeviceName = (deviceName: string) => {
- setDeviceName(deviceName)
+ const onChangeDeviceName = (nextDeviceName: string) => {
+ setDeviceName(nextDeviceName)
setReadyToShowError(false)
_setReadyToShowError(true)
}
- const onContinue = () => (disabled ? {} : props.onContinue(cleanDeviceName))
+ const onContinue = () => {
+ if (disabled) {
+ return
+ }
+
+ props.onContinue(cleanDeviceName)
+ }
React.useEffect(() => {
if (cleanDeviceName !== deviceName) {
@@ -97,7 +107,7 @@ const EnterDevicename = (props: Props) => {
error={showDisabled}
maxLength={64}
placeholder="Name"
- onChangeText={_setDeviceName}
+ onChangeText={onChangeDeviceName}
onEnterKeyDown={onContinue}
value={deviceName}
/>
diff --git a/shared/signup/email.tsx b/shared/signup/email.tsx
index 80f741f1f361..4e5c6b39e2ec 100644
--- a/shared/signup/email.tsx
+++ b/shared/signup/email.tsx
@@ -4,34 +4,27 @@ import * as Kb from '@/common-adapters'
import {SignupScreen, errorBanner} from './common'
import {useSettingsEmailState} from '@/stores/settings-email'
import {useSignupState} from '@/stores/signup'
-import {usePushState} from '@/stores/push'
+import {useCompleteSignupWithEmail, useSkipSignupEmail} from './navigation'
const ConnectedEnterEmail = () => {
- const _showPushPrompt = usePushState(s => C.isMobile && !s.hasPermissions && s.showPushPrompt)
- const addedEmail = useSettingsEmailState(s => s.addedEmail)
- const error = useSettingsEmailState(s => s.error)
+ const {addEmail, addedEmail, error} = useSettingsEmailState(
+ C.useShallow(s => ({
+ addEmail: s.dispatch.addEmail,
+ addedEmail: s.addedEmail,
+ error: s.error,
+ }))
+ )
const initialEmail = useSignupState(s => s.email)
const waiting = C.Waiting.useAnyWaiting(C.addEmailWaitingKey)
- const clearModals = C.useRouterState(s => s.dispatch.clearModals)
- const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
- const setJustSignedUpEmail = useSignupState(s => s.dispatch.setJustSignedUpEmail)
- const _onSkip = () => {
- setJustSignedUpEmail(C.noEmail)
- }
- const _onSuccess = setJustSignedUpEmail
+ const onSkip = useSkipSignupEmail()
+ const onCompleteSignupWithEmail = useCompleteSignupWithEmail()
- const addEmail = useSettingsEmailState(s => s.dispatch.addEmail)
- const onSkip = () => {
- _onSkip()
- _showPushPrompt ? navigateAppend('settingsPushPrompt', true) : clearModals()
- }
const [addEmailInProgress, setAddEmailInProgress] = React.useState('')
React.useEffect(() => {
- if (addedEmail === addEmailInProgress) {
- _onSuccess(addEmailInProgress)
- _showPushPrompt ? navigateAppend('settingsPushPrompt', true) : clearModals()
+ if (addEmailInProgress && addedEmail === addEmailInProgress) {
+ onCompleteSignupWithEmail(addedEmail)
}
- }, [addedEmail, addEmailInProgress, _onSuccess, _showPushPrompt, navigateAppend, clearModals])
+ }, [addedEmail, addEmailInProgress, onCompleteSignupWithEmail])
const onCreate = (email: string, searchable: boolean) => {
addEmail(email, searchable)
@@ -40,8 +33,15 @@ const ConnectedEnterEmail = () => {
const [email, onChangeEmail] = React.useState(initialEmail || '')
const [searchable, onChangeSearchable] = React.useState(true)
- const disabled = !email.trim()
- const onContinue = () => (disabled ? {} : onCreate(email.trim(), searchable))
+ const emailTrimmed = email.trim()
+ const disabled = !emailTrimmed
+ const onContinue = () => {
+ if (disabled) {
+ return
+ }
+
+ onCreate(emailTrimmed, searchable)
+ }
return (
{
rightActionLabel="Skip"
onRightAction={onSkip}
title="Your email address"
- showHeaderInfoicon={true}
+ showHeaderInfoIcon={true}
>
{
)
}
-export type Props = {
- error: string
- initialEmail: string
- onCreate: (email: string, searchable: boolean) => void
- onSkip?: () => void
- waiting: boolean
-}
-
type BodyProps = {
onChangeEmail: (email: string) => void
onContinue: () => void
diff --git a/shared/signup/feedback.tsx b/shared/signup/feedback.tsx
index 1bc404e8c6a3..8e0fdc3a24fe 100644
--- a/shared/signup/feedback.tsx
+++ b/shared/signup/feedback.tsx
@@ -11,27 +11,26 @@ const SignupFeedback = () => {
const loggedOut = useConfigState(s => !s.loggedIn)
const sending = C.Waiting.useAnyWaiting(C.waitingKeySettingsSendFeedback)
const navigateUp = C.useRouterState(s => s.dispatch.navigateUp)
- const onBack = () => {
- navigateUp()
- }
+ const onBack = () => navigateUp()
const [feedbackSent, setFeedbackSent] = React.useState(false)
+ const banners = (
+ <>
+ {feedbackSent ? (
+
+
+
+ ) : null}
+ {sendError ? errorBanner(sendError) : null}
+ >
+ )
return (
- {feedbackSent ? (
-
-
-
- ) : null}
- {sendError ? errorBanner(sendError) : null}
- >
- }
+ banners={banners}
title="Send feedback"
onBack={onBack}
- showHeaderInfoicon={false}
- showHeaderInfoiconRow={!loggedOut}
+ showHeaderInfoIcon={false}
+ showHeaderInfoIconRow={!loggedOut}
>
{
+ const showPushPrompt = usePushState(s => C.isMobile && !s.hasPermissions && s.showPushPrompt)
+ const clearModals = C.useRouterState(s => s.dispatch.clearModals)
+ const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
+
+ return React.useCallback(() => {
+ showPushPrompt ? navigateAppend('settingsPushPrompt', true) : clearModals()
+ }, [showPushPrompt, navigateAppend, clearModals])
+}
+
+export const useCompleteSignupWithEmail = () => {
+ const finishSignup = useFinishSignup()
+ const setJustSignedUpEmail = useSignupState(s => s.dispatch.setJustSignedUpEmail)
+
+ return React.useCallback(
+ (email: string) => {
+ setJustSignedUpEmail(email)
+ finishSignup()
+ },
+ [setJustSignedUpEmail, finishSignup]
+ )
+}
+
+export const useSkipSignupEmail = () => {
+ const completeSignupWithEmail = useCompleteSignupWithEmail()
+
+ return React.useCallback(() => {
+ completeSignupWithEmail(C.noEmail)
+ }, [completeSignupWithEmail])
+}
+
+export const useNavigateToSignupEmail = () => {
+ const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
+ const clearPhoneNumberAdd = useSettingsPhoneState(s => s.dispatch.clearPhoneNumberAdd)
+
+ return React.useCallback(() => {
+ clearPhoneNumberAdd()
+ navigateAppend('signupEnterEmail', true)
+ }, [clearPhoneNumberAdd, navigateAppend])
+}
diff --git a/shared/signup/phone-number/index.tsx b/shared/signup/phone-number/index.tsx
index d0c948ea3601..f08d1a40a56f 100644
--- a/shared/signup/phone-number/index.tsx
+++ b/shared/signup/phone-number/index.tsx
@@ -2,6 +2,7 @@ import * as C from '@/constants'
import * as React from 'react'
import * as Kb from '@/common-adapters'
import {SignupScreen, errorBanner} from '../common'
+import {useNavigateToSignupEmail} from '../navigation'
import {useSettingsPhoneState} from '@/stores/settings-phone'
type BodyProps = {
@@ -71,25 +72,32 @@ const styles = Kb.Styles.styleSheetCreate(() => ({
}))
const ConnectedEnterPhoneNumber = () => {
- const defaultCountry = useSettingsPhoneState(s => s.defaultCountry)
- const error = useSettingsPhoneState(s => s.error)
- const pendingVerification = useSettingsPhoneState(s => s.pendingVerification)
+ const {
+ addPhoneNumber,
+ clearPhoneNumberErrors,
+ defaultCountry,
+ error,
+ loadDefaultPhoneCountry,
+ pendingVerification,
+ } = useSettingsPhoneState(
+ C.useShallow(s => ({
+ addPhoneNumber: s.dispatch.addPhoneNumber,
+ clearPhoneNumberErrors: s.dispatch.clearPhoneNumberErrors,
+ defaultCountry: s.defaultCountry,
+ error: s.error,
+ loadDefaultPhoneCountry: s.dispatch.loadDefaultPhoneCountry,
+ pendingVerification: s.pendingVerification,
+ }))
+ )
const waiting = C.Waiting.useAnyWaiting(C.waitingKeySettingsPhoneAddPhoneNumber)
- const clearPhoneNumberErrors = useSettingsPhoneState(s => s.dispatch.clearPhoneNumberErrors)
- const clearPhoneNumberAdd = useSettingsPhoneState(s => s.dispatch.clearPhoneNumberAdd)
- const onClear = clearPhoneNumberErrors
- const addPhoneNumber = useSettingsPhoneState(s => s.dispatch.addPhoneNumber)
const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
- const onSkip = () => {
- clearPhoneNumberAdd()
- navigateAppend('signupEnterEmail', true)
- }
+ const onSkip = useNavigateToSignupEmail()
React.useEffect(() => {
return () => {
- onClear()
+ clearPhoneNumberErrors()
}
- }, [onClear])
+ }, [clearPhoneNumberErrors])
const lastPendingVerificationRef = React.useRef(pendingVerification)
React.useEffect(() => {
@@ -100,15 +108,22 @@ const ConnectedEnterPhoneNumber = () => {
}, [pendingVerification, error, navigateAppend])
// trigger a default phone number country rpc if it's not already loaded
- const loadDefaultPhoneCountry = useSettingsPhoneState(s => s.dispatch.loadDefaultPhoneCountry)
React.useEffect(() => {
- !defaultCountry && loadDefaultPhoneCountry()
+ if (!defaultCountry) {
+ loadDefaultPhoneCountry()
+ }
}, [defaultCountry, loadDefaultPhoneCountry])
const [phoneNumber, onChangePhoneNumber] = React.useState('')
const [valid, onChangeValidity] = React.useState(false)
const disabled = !valid
- const onContinue = () => (disabled || waiting ? {} : addPhoneNumber(phoneNumber, true /* searchable */))
+ const onContinue = () => {
+ if (disabled || waiting) {
+ return
+ }
+
+ addPhoneNumber(phoneNumber, true)
+ }
const onChangeNumberCb = (phoneNumber: string, validity: boolean) => {
onChangePhoneNumber(phoneNumber)
onChangeValidity(validity)
@@ -128,7 +143,7 @@ const ConnectedEnterPhoneNumber = () => {
rightActionLabel="Skip"
onRightAction={onSkip}
title="Your phone number"
- showHeaderInfoicon={true}
+ showHeaderInfoIcon={true}
>
void
- code: string
onResend: () => void
resendWaiting: boolean
}
diff --git a/shared/signup/phone-number/verify.tsx b/shared/signup/phone-number/verify.tsx
index 83fc1ef27533..d7a51d86ff1a 100644
--- a/shared/signup/phone-number/verify.tsx
+++ b/shared/signup/phone-number/verify.tsx
@@ -1,40 +1,31 @@
import * as C from '@/constants'
import * as React from 'react'
import * as Kb from '@/common-adapters'
-import {SignupScreen} from '../common'
+import {SignupScreen, errorBanner} from '../common'
import {e164ToDisplay} from '@/util/phone-numbers'
import VerifyBody from './verify-body'
import {useSettingsPhoneState} from '@/stores/settings-phone'
const Container = () => {
- const error = useSettingsPhoneState(s => (s.verificationState === 'error' ? s.error : ''))
- const phoneNumber = useSettingsPhoneState(s => s.pendingVerification)
+ const {clearPhoneNumberAdd, error, phoneNumber, resendVerificationForPhone, verificationStatus, verifyPhoneNumber} =
+ useSettingsPhoneState(
+ C.useShallow(s => ({
+ clearPhoneNumberAdd: s.dispatch.clearPhoneNumberAdd,
+ error: s.verificationState === 'error' ? s.error : '',
+ phoneNumber: s.pendingVerification,
+ resendVerificationForPhone: s.dispatch.resendVerificationForPhone,
+ verificationStatus: s.verificationState,
+ verifyPhoneNumber: s.dispatch.verifyPhoneNumber,
+ }))
+ )
const resendWaiting = C.Waiting.useAnyWaiting([
C.waitingKeySettingsPhoneResendVerification,
C.waitingKeySettingsPhoneAddPhoneNumber,
])
- const verificationStatus = useSettingsPhoneState(s => s.verificationState)
const verifyWaiting = C.Waiting.useAnyWaiting(C.waitingKeySettingsPhoneVerifyPhoneNumber)
-
- const verifyPhoneNumber = useSettingsPhoneState(s => s.dispatch.verifyPhoneNumber)
- const resendVerificationForPhone = useSettingsPhoneState(s => s.dispatch.resendVerificationForPhone)
-
- const clearPhoneNumberAdd = useSettingsPhoneState(s => s.dispatch.clearPhoneNumberAdd)
-
- const _onContinue = (phoneNumber: string, code: string) => {
- verifyPhoneNumber(phoneNumber, code)
- }
- const _onResend = (phoneNumber: string) => {
- resendVerificationForPhone(phoneNumber)
- }
const navigateUp = C.useRouterState(s => s.dispatch.navigateUp)
- const onBack = () => {
- navigateUp()
- }
- const onCleanup = clearPhoneNumberAdd
const onSuccess = C.useRouterState(s => s.dispatch.clearModals)
- const ponContinue = (code: string) => _onContinue(phoneNumber, code)
- const onResend = () => _onResend(phoneNumber)
+ const onBack = () => navigateUp()
React.useEffect(() => {
if (verificationStatus === 'success') {
@@ -44,29 +35,25 @@ const Container = () => {
React.useEffect(() => {
return () => {
- onCleanup()
+ clearPhoneNumberAdd()
}
- }, [onCleanup])
+ }, [clearPhoneNumberAdd])
const [code, onChangeCode] = React.useState('')
- const disabled = !code
- const onContinue = disabled
- ? () => {}
- : () => {
- ponContinue(code)
- }
+ const onContinue = () => {
+ if (!code) {
+ return
+ }
+
+ verifyPhoneNumber(phoneNumber, code)
+ }
+ const onResend = () => resendVerificationForPhone(phoneNumber)
const displayPhone = e164ToDisplay(phoneNumber)
return (
-
-
- ) : null
- }
+ banners={errorBanner(error)}
buttons={[{label: 'Continue', onClick: onContinue, type: 'Success', waiting: verifyWaiting}]}
titleComponent={
@@ -87,9 +74,9 @@ const Container = () => {
}
negativeHeader={true}
- showHeaderInfoicon={true}
+ showHeaderInfoIcon={true}
>
-
+
)
}
diff --git a/shared/signup/routes.tsx b/shared/signup/routes.tsx
index 4cbcd42e2136..0cd710fbac8e 100644
--- a/shared/signup/routes.tsx
+++ b/shared/signup/routes.tsx
@@ -1,40 +1,23 @@
import * as React from 'react'
-import * as C from '@/constants'
import * as Kb from '@/common-adapters'
import {InfoIcon} from './common'
-import {useSignupState} from '@/stores/signup'
-import {useSettingsPhoneState} from '@/stores/settings-phone'
-import {usePushState} from '@/stores/push'
+import {useNavigateToSignupEmail, useSkipSignupEmail} from './navigation'
const EmailSkipButton = () => {
- const showPushPrompt = usePushState(s => C.isMobile && !s.hasPermissions && s.showPushPrompt)
- const clearModals = C.useRouterState(s => s.dispatch.clearModals)
- const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
- const setJustSignedUpEmail = useSignupState(s => s.dispatch.setJustSignedUpEmail)
+ const onSkip = useSkipSignupEmail()
+
return (
- {
- setJustSignedUpEmail(C.noEmail)
- showPushPrompt ? navigateAppend('settingsPushPrompt', true) : clearModals()
- }}
- >
+
Skip
)
}
const PhoneSkipButton = () => {
- const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend)
- const clearPhoneNumberAdd = useSettingsPhoneState(s => s.dispatch.clearPhoneNumberAdd)
+ const onSkip = useNavigateToSignupEmail()
+
return (
- {
- clearPhoneNumberAdd()
- navigateAppend('signupEnterEmail', true)
- }}
- >
+
Skip
)
diff --git a/shared/signup/username.tsx b/shared/signup/username.tsx
index fca50d729add..878f7799164d 100644
--- a/shared/signup/username.tsx
+++ b/shared/signup/username.tsx
@@ -6,33 +6,35 @@ import {useSignupState} from '@/stores/signup'
import {useProvisionState} from '@/stores/provision'
const ConnectedEnterUsername = () => {
- const error = useSignupState(s => s.usernameError)
- const initialUsername = useSignupState(s => s.username)
- const usernameTaken = useSignupState(s => s.usernameTaken)
- const checkUsername = useSignupState(s => s.dispatch.checkUsername)
+ const {checkUsername, error, initialUsername, resetState, usernameTaken} = useSignupState(
+ C.useShallow(s => ({
+ checkUsername: s.dispatch.checkUsername,
+ error: s.usernameError,
+ initialUsername: s.username,
+ resetState: s.dispatch.resetState,
+ usernameTaken: s.usernameTaken,
+ }))
+ )
const waiting = C.Waiting.useAnyWaiting(C.waitingKeySignup)
const navigateUp = C.useRouterState(s => s.dispatch.navigateUp)
- const resetState = useSignupState(s => s.dispatch.resetState)
+
const onBack = () => {
resetState()
navigateUp()
}
- const onContinue = checkUsername
-
const startProvision = useProvisionState(s => s.dispatch.startProvision)
- const onLogin = (initUsername: string) => {
- startProvision(initUsername)
- }
- const props = {
- error,
- initialUsername,
- onBack,
- onContinue,
- onLogin,
- usernameTaken,
- waiting,
- }
- return
+
+ return (
+
+ )
}
type Props = {
@@ -55,23 +57,24 @@ const EnterUsername = (props: Props) => {
if (disabled) {
return
}
+
onChangeUsername(usernameTrimmed) // maybe trim the input
props.onContinue(usernameTrimmed)
}
- const eulaLabel = (
-
- I accept the{' '}
-
- Keybase Acceptable Use Policy
-
-
- )
+ const eulaTextType = Kb.Styles.isMobile ? 'BodySmall' : 'Body'
+ const eulaLinkType = Kb.Styles.isMobile ? 'BodySmallPrimaryLink' : 'BodyPrimaryLink'
const eulaBlock = (
- setAcceptedEULA(s => !s)} />
+
+ I accept the Keybase Acceptable Use Policy
+
+ }
+ checked={acceptedEULA}
+ onCheck={() => setAcceptedEULA(s => !s)}
+ />
)
+
return (