Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallet settings, change password and download mnemonic #4326

Merged
merged 27 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4fdd70f
wip
drillprop Jun 6, 2023
d9e5829
settings view ground work
drillprop Jun 7, 2023
0284723
uncomment route
drillprop Jun 12, 2023
704e4f6
add borderColor prop to banner
drillprop Jun 13, 2023
ba03531
fix issues with modal paddings
drillprop Jun 13, 2023
53ea0e5
create password form component, sign up modal refactoring
drillprop Jun 13, 2023
b52a6bc
add export seed dialog and change password dialog
drillprop Jun 13, 2023
1ea0524
handle copy button
drillprop Jun 13, 2023
d424d26
Merge remote-tracking branch 'upstream/accounts' into 4243-wallet-set…
drillprop Jun 14, 2023
b2d2278
show different settings for wallet users
drillprop Jun 14, 2023
7cc3ca7
refactor password dialogs
drillprop Jun 14, 2023
7d061d0
cr fixes
drillprop Jun 15, 2023
8f252a0
Update packages/atlas/src/views/viewer/MembershipSettingsView/Members…
drillprop Jun 15, 2023
06281d7
auth helpers further refactor
drillprop Jun 15, 2023
9319c75
reset form on cancel
drillprop Jun 15, 2023
794f052
Merge remote-tracking branch 'upstream/accounts' into 4243-wallet-set…
drillprop Jun 15, 2023
7e00913
cr fixes
drillprop Jun 15, 2023
379b5f2
cr fixes 2
drillprop Jun 15, 2023
48c9770
add additional step to ChangePassword
drillprop Jun 19, 2023
76920c3
action bar adjustments, add unsaved changes dialog
drillprop Jun 21, 2023
6bb6468
fix viewer layout
drillprop Jun 21, 2023
ed044d5
action bar changes
drillprop Jun 26, 2023
5e169be
Merge remote-tracking branch 'upstream/accounts' into 4243-wallet-set…
drillprop Jun 26, 2023
bd5cd82
action bar adjustments
drillprop Jun 26, 2023
309dbdb
cr fixes
drillprop Jun 27, 2023
ba4af11
fix fee issue
drillprop Jun 27, 2023
3e0a8b6
change password fix?
drillprop Jun 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/atlas/src/components/ActionBar/ActionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import BN from 'bn.js'
import { MouseEvent, forwardRef } from 'react'
import { MouseEvent, forwardRef, useRef } from 'react'
import { CSSTransition } from 'react-transition-group'

import { Fee } from '@/components/Fee'
import { Text } from '@/components/Text'
import { TooltipProps } from '@/components/Tooltip'
import { Tooltip, TooltipProps } from '@/components/Tooltip'
import { ButtonProps } from '@/components/_buttons/Button'
import { useHasEnoughBalance } from '@/hooks/useHasEnoughBalance'
import { useMediaMatch } from '@/hooks/useMediaMatch'
Expand Down Expand Up @@ -34,6 +34,7 @@ export type ActionBarProps = {
feeLoading?: boolean
infoBadge?: ActionDialogInfoBadge
primaryButton: ActionDialogButtonProps
primaryButtonTooltip?: Omit<TooltipProps, 'reference'>
secondaryButton?: ActionDialogButtonProps
isActive?: boolean
skipFeeCheck?: boolean
Expand All @@ -49,6 +50,7 @@ export const ActionBar = forwardRef<HTMLDivElement, ActionBarProps>(
isActive = true,
className,
primaryButton,
primaryButtonTooltip,
secondaryButton,
infoBadge,
skipFeeCheck,
Expand All @@ -63,6 +65,7 @@ export const ActionBar = forwardRef<HTMLDivElement, ActionBarProps>(
primaryButton.onClick,
skipFeeCheck
)
const buttonRef = useRef<HTMLButtonElement>(null)

return (
<ActionBarContainer ref={ref} className={className} isActive={isActive}>
Expand Down Expand Up @@ -92,6 +95,7 @@ export const ActionBar = forwardRef<HTMLDivElement, ActionBarProps>(
</CSSTransition>
<ActionButtonPrimary
{...primaryButton}
ref={buttonRef}
disabled={primaryButton.disabled || loadingState}
onClick={isNoneCrypto ? primaryButton.onClick : signTransactionHandler}
secondaryButtonExists={!!secondaryButton}
Expand All @@ -100,6 +104,7 @@ export const ActionBar = forwardRef<HTMLDivElement, ActionBarProps>(
>
{loadingState ? 'Please wait...' : primaryButton.text}
</ActionButtonPrimary>
{primaryButtonTooltip && <Tooltip reference={buttonRef.current} {...primaryButtonTooltip} />}
</ActionBarContainer>
)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/atlas/src/components/Banner/Banner.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ export const BannerDescription = styled.div<{ withTitle?: boolean }>`
white-space: pre-line;
`

export const BannerWrapper = styled.div<{ size: 'small' | 'medium' }>`
export const BannerWrapper = styled.div<{ size: 'small' | 'medium'; borderColor?: string }>`
flex: 1;
position: relative;
padding: ${(props) => (props.size === 'small' ? sizes(4) : sizes(6))};
width: 100%;
background-color: ${cVar('colorBackgroundMutedAlpha')};
border-left: 2px solid ${cVar('colorBorderPrimary')};
border-left: 2px solid ${({ borderColor }) => borderColor ?? cVar('colorBorderPrimary')};
`

export const ActionButton = styled(Button)`
Expand Down
4 changes: 3 additions & 1 deletion packages/atlas/src/components/Banner/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type ActionButtonProps = {

export type BannerProps = {
dismissibleId?: string
borderColor?: string
title?: string
description?: ReactNode
className?: string
Expand All @@ -32,6 +33,7 @@ export type BannerProps = {

export const Banner: FC<BannerProps> = ({
title,
borderColor,
description,
className,
icon,
Expand All @@ -49,7 +51,7 @@ export const Banner: FC<BannerProps> = ({
}

return (
<BannerWrapper size={size} className={className}>
<BannerWrapper size={size} className={className} borderColor={borderColor}>
<Container>
<div>
{title && (
Expand Down
17 changes: 3 additions & 14 deletions packages/atlas/src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,13 @@ import { CSSTransition } from 'react-transition-group'

import { transitions } from '@/styles'

import {
IconWrapper,
StyledSvgAlertsInformative24,
TooltipContainer,
TooltipContent,
TooltipHeader,
TooltipText,
} from './Tooltip.styles'
import { IconWrapper, TooltipContainer, TooltipContent, TooltipHeader, TooltipText } from './Tooltip.styles'

type Placement = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'top' | 'bottom'
export type TooltipProps = PropsWithChildren<{
text?: string
headerText?: string
icon?: boolean
icon?: ReactNode
placement?: Placement
offsetX?: number
offsetY?: number
Expand Down Expand Up @@ -59,11 +52,7 @@ export const Tooltip: FC<TooltipProps> = ({
) : (
<TooltipContent headerText={!!headerText}>
<TooltipHeader headerText={!!headerText}>
{icon && (
<IconWrapper>
<StyledSvgAlertsInformative24 />
</IconWrapper>
)}
{icon && <IconWrapper>{icon}</IconWrapper>}
{headerText && (
<TooltipText as="span" variant="h100">
{headerText}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import styled from '@emotion/styled'

import { Text } from '@/components/Text'
import { sizes } from '@/styles'

export const PasswordRequirementsWrapper = styled.section`
display: grid;
gap: ${sizes(2)};
`

export const PasswordRequirementsList = styled.ul`
padding: 0;
margin: 0;
list-style-type: none;
display: grid;
gap: ${sizes(2)};
`

export const PasswordRequirementItem = styled(Text)`
display: flex;
align-items: center;
gap: ${sizes(2)};
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { get } from 'lodash-es'
import { FC, useEffect, useState } from 'react'
import { useFormContext } from 'react-hook-form'

import { SvgActionCheck, SvgActionClose, SvgActionMinus } from '@/assets/icons'
import { IconWrapper } from '@/components/IconWrapper'
import { Text } from '@/components/Text'
import { cVar } from '@/styles'

import {
PasswordRequirementItem,
PasswordRequirementsList,
PasswordRequirementsWrapper,
} from './PasswordCriterias.styles'

type ValidationState = 'pending' | 'success' | 'error'

type PasswordRequirementsErrors = {
length: ValidationState
upperCase: ValidationState
number: ValidationState
specialCharacter: ValidationState
}

const getValidationState = (isError: boolean) => {
if (isError) {
return 'error'
} else {
return 'success'
}
}

const PASSWORD_REQUIREMENTS_ERRORS_INITIAL_STATE: PasswordRequirementsErrors = {
length: 'pending',
upperCase: 'pending',
number: 'pending',
specialCharacter: 'pending',
}

type PasswordCriteriaProps = {
validationState: ValidationState
text: string
}

const getIconWrapperProps = (state: ValidationState) => {
switch (state) {
case 'pending':
return {
backgroundColor: undefined,
icon: <SvgActionMinus />,
}
case 'success':
return {
backgroundColor: cVar('colorBackgroundSuccess'),
icon: <SvgActionCheck />,
}
case 'error':
return {
backgroundColor: cVar('colorBackgroundError'),
icon: <SvgActionClose />,
}
}
}

export const PasswordCriteria: FC<PasswordCriteriaProps> = ({ text, validationState }) => {
const iconWrapperProps = getIconWrapperProps(validationState)
return (
<PasswordRequirementItem as="li" color="colorText" variant="t200">
<IconWrapper size="small" {...iconWrapperProps} /> {text}
</PasswordRequirementItem>
)
}

type PasswordCriteriasProps = {
path?: string
}

export const PasswordCriterias: FC<PasswordCriteriasProps> = ({ path = 'password' }) => {
const context = useFormContext()
const [passwordRequirementsErrors, setPasswordRequirementsErrors] = useState<PasswordRequirementsErrors>(
PASSWORD_REQUIREMENTS_ERRORS_INITIAL_STATE
)

if (!context) {
throw new Error('PasswordCriterias can be used only within FormProvider with password as field')
}
const { watch } = context

useEffect(() => {
const subscription = watch((fields) => {
const password = get(fields, path)
if (!password) {
setPasswordRequirementsErrors(PASSWORD_REQUIREMENTS_ERRORS_INITIAL_STATE)
return
}

setPasswordRequirementsErrors(() => ({
length: getValidationState(password.length <= 9 || password.length >= 64),
number: getValidationState(/^[^0-9]*$/.test(password)),
upperCase: getValidationState(/^[^A-Z]*$/.test(password)),
specialCharacter: getValidationState(/^[^!@#$%^&*()_+]*$/.test(password)),
}))
})
return () => subscription.unsubscribe()
}, [path, watch])

return (
<PasswordRequirementsWrapper>
<Text as="h2" variant="h200">
Password must:
</Text>
<PasswordRequirementsList>
<PasswordCriteria validationState={passwordRequirementsErrors.length} text="Be between 9 and 64 character" />
<PasswordCriteria
validationState={passwordRequirementsErrors.upperCase}
text="Include an uppercase character"
/>
<PasswordCriteria validationState={passwordRequirementsErrors.number} text="Include a number" />
<PasswordCriteria
validationState={passwordRequirementsErrors.specialCharacter}
text="Include a special character (eg. !, ?, %)"
/>
</PasswordRequirementsList>
</PasswordRequirementsWrapper>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './PasswordCriterias'
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ export const SignUpModal = () => {
{currentStep === SignUpSteps.SignUpEmail && (
<SignUpEmailStep
{...commonProps}
isOverflowing={overflow || !smMatch}
isEmailAlreadyTakenError={emailAlreadyTakenError}
onEmailSubmit={handleEmailStepSubmit}
email={signUpFormData.email}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ type EmailStepForm = z.infer<typeof zodSchema>
type SignUpEmailStepProps = {
onEmailSubmit: (email: string, confirmedTerms: boolean) => void
isEmailAlreadyTakenError?: boolean
isOverflowing: boolean
} & SignUpStepsCommonProps &
Pick<AccountFormData, 'email' | 'confirmedTerms'>

export const SignUpEmailStep: FC<SignUpEmailStepProps> = ({
setPrimaryButtonProps,
hasNavigatedBack,
isEmailAlreadyTakenError,
isOverflowing,
onEmailSubmit,
confirmedTerms,
email,
Expand Down Expand Up @@ -94,7 +96,7 @@ export const SignUpEmailStep: FC<SignUpEmailStepProps> = ({
hasNavigatedBack={hasNavigatedBack}
subtitle="If you misspell your email address, please note that there is no option for us to recover your account."
>
<StyledSignUpForm>
<StyledSignUpForm additionalPaddingBottom={!isOverflowing}>
<FormField label="Email" error={errors.email?.message}>
<Input {...register('email')} placeholder="Email" error={!!errors.email} />
</FormField>
Expand Down
Loading