Skip to content

Commit

Permalink
Create component to display words, use when creating and revealing pa…
Browse files Browse the repository at this point in the history
…ssphrase.
  • Loading branch information
matthewcarlreetz committed Sep 18, 2023
1 parent 9bff185 commit 1d92d7b
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 235 deletions.
140 changes: 140 additions & 0 deletions src/components/RevealWords.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { memo, useCallback, useMemo } from 'react'
import { upperCase } from 'lodash'
import { useTranslation } from 'react-i18next'
import Text from '@components/Text'
import { ReAnimatedBox } from '@components/AnimatedBox'
import Box from '@components/Box'
import ButtonPressable from '@components/ButtonPressable'
import { FlatList } from 'react-native'
import { useColors, useSpacing } from '@theme/themeHooks'
import CopyAddress from '@assets/images/copyAddress.svg'
import TouchableOpacityBox from '@components/TouchableOpacityBox'
import useCopyText from '@hooks/useCopyText'
import useHaptic from '@hooks/useHaptic'
import { FadeIn } from 'react-native-reanimated'

type Props = {
mnemonic: string[]
onDone: () => void
ListHeaderComponent?: React.JSX.Element

Check failure on line 19 in src/components/RevealWords.tsx

View workflow job for this annotation

GitHub Actions / build

Namespace 'React' has no exported member 'JSX'.
}
const RevealWords = ({ mnemonic, onDone, ListHeaderComponent }: Props) => {
const { t } = useTranslation()
const spacing = useSpacing()
const { secondaryText } = useColors()
const copyText = useCopyText()
const { triggerImpact } = useHaptic()

const handleCopySeedPhrase = useCallback(() => {
triggerImpact('light')
copyText({
message: t('generic.copiedSeedPhrase'),
copyText: mnemonic.join(' ') || '',
})
}, [copyText, triggerImpact, mnemonic, t])

const renderItem = useCallback(
// eslint-disable-next-line react/no-unused-prop-types
({ item, index }: { item: string; index: number }) => {
return (
<Box
borderRadius="round"
padding="s"
marginHorizontal="s"
marginBottom="m"
flex={1}
overflow="hidden"
backgroundColor="surfaceSecondary"
alignItems="center"
justifyContent="center"
flexDirection="row"
>
<Text
fontSize={16}
color="primaryText"
maxFontSizeMultiplier={1}
adjustsFontSizeToFit
>{`${index + 1}. `}</Text>
<Text
fontSize={16}
color="primaryText"
maxFontSizeMultiplier={1}
adjustsFontSizeToFit
>
{upperCase(item)}
</Text>
</Box>
)
},
[],
)

const contentContainerStyle = useMemo(
() => ({
flexGrow: 1,
paddingHorizontal: spacing.l,
}),
[spacing],
)

const ListFooterComponent = useCallback(() => {
return (
<Box>
<TouchableOpacityBox
onPress={handleCopySeedPhrase}
justifyContent="center"
alignItems="center"
flexDirection="row"
marginTop="m"
marginBottom="xl"
>
<CopyAddress width={16} height={16} color={secondaryText} />
<Text
marginStart="s"
variant="body2"
color="secondaryText"
numberOfLines={1}
adjustsFontSizeToFit
maxFontSizeMultiplier={1.2}
textAlign="center"
>
{t('generic.copyToClipboard')}
</Text>
</TouchableOpacityBox>
<Box flex={1} />
<ButtonPressable
height={60}
borderRadius="round"
backgroundColor="surfaceSecondary"
titleColor="primaryText"
title={t('settings.revealWords.next')}
marginBottom="m"
onPress={onDone}
/>
</Box>
)
}, [handleCopySeedPhrase, secondaryText, t, onDone])

return (
<ReAnimatedBox flex={1} entering={FadeIn.delay(300)}>
<FlatList
numColumns={2}
columnWrapperStyle={{
flexDirection: 'row',
}}
data={mnemonic}
ListHeaderComponent={ListHeaderComponent}
ListFooterComponent={ListFooterComponent}
ListFooterComponentStyle={{
flexGrow: 1,
justifyContent: 'flex-end',
}}
renderItem={renderItem}
contentContainerStyle={contentContainerStyle}
scrollEnabled
/>
</ReAnimatedBox>
)
}

export default memo(RevealWords)
135 changes: 22 additions & 113 deletions src/features/onboarding/create/AccountCreatePassphraseScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import React, { useState, useEffect, useCallback, memo } from 'react'
import React, { useCallback, memo, useMemo } from 'react'
import { useNavigation } from '@react-navigation/native'
import { useTranslation } from 'react-i18next'
import { useAsync } from 'react-async-hook'
import Carousel, { Pagination } from 'react-native-snap-carousel'
import { upperCase } from 'lodash'
import Close from '@assets/images/close.svg'
import InfoError from '@assets/images/infoError.svg'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import Box from '@components/Box'
import Text from '@components/Text'
import { wp } from '@utils/layout'
import { useAccountStorage } from '@storage/AccountStorageProvider'
import ButtonPressable from '@components/ButtonPressable'
import { useColors } from '@theme/themeHooks'
import TouchableOpacityBox from '@components/TouchableOpacityBox'
import RevealWords from '@components/RevealWords'
import { CreateAccountNavigationProp } from './createAccountNavTypes'
import { useOnboarding } from '../OnboardingProvider'
import { OnboardingNavigationProp } from '../onboardingTypes'
Expand All @@ -28,35 +24,11 @@ const AccountCreatePassphraseScreen = () => {
} = useOnboarding()
const parentNav = useNavigation<OnboardingNavigationProp>()
const navigation = useNavigation<CreateAccountNavigationProp>()
const [wordIndex, setWordIndex] = useState(0)
const [disabled, setDisabled] = useState(true)
const [viewedWords, setViewedWords] = useState(new Array(24).fill(false))
const { bottom } = useSafeAreaInsets()
const { result: secureAccount } = useAsync(
async () => createSecureAccount({ netType, use24Words: true }),
[createSecureAccount, netType],
)

const onSnapToItem = useCallback(
(index: number) => {
setWordIndex(index)
setViewedWords(
Object.assign(new Array(24).fill(false), viewedWords, {
0: true,
[index]: true,
}),
)
},
[viewedWords],
)

useEffect(() => {
const viewedAll = viewedWords.every((w) => w)
if (!viewedAll && !__DEV__) return

setDisabled(false)
}, [viewedWords])

const navToTop = useCallback(() => {
if (hasAccounts) {
parentNav.popToTop()
Expand All @@ -72,44 +44,16 @@ const AccountCreatePassphraseScreen = () => {
navigation.navigate('AccountEnterPassphraseScreen')
}, [navigation, secureAccount, setOnboardingData])

const renderItem = ({ item, index }: { item: string; index: number }) => {
const isFirst = index === 0
const isLast = index + 1 === secureAccount?.mnemonic?.length
const ListHeaderComponent = useMemo(() => {
return (
<Box
marginHorizontal="s"
marginLeft={isFirst ? 'l' : undefined}
marginRight={isLast ? 'l' : undefined}
flex={1}
overflow="hidden"
backgroundColor="transparent10"
paddingHorizontal="l"
alignItems="center"
justifyContent="center"
flexDirection="row"
borderRadius="xl"
>
<Text variant="h3" color="secondaryText" maxFontSizeMultiplier={1}>
{`${index + 1}. `}
</Text>
<Text
variant="h3"
color="primaryText"
maxFontSizeMultiplier={1}
letterSpacing={1.5}
<>
<TouchableOpacityBox
padding="l"
onPress={navToTop}
alignItems="flex-end"
>
{upperCase(item)}
</Text>
</Box>
)
}

return (
<Box flex={1} backgroundColor="secondaryBackground">
<TouchableOpacityBox padding="l" onPress={navToTop} alignItems="flex-end">
<Close color={colors.primaryText} height={16} width={16} />
</TouchableOpacityBox>
<Box flex={1} marginTop="xl">
<Close color={colors.primaryText} height={16} width={16} />
</TouchableOpacityBox>
<Box justifyContent="center" alignItems="center" marginBottom="xl">
<InfoError />
</Box>
Expand All @@ -129,57 +73,22 @@ const AccountCreatePassphraseScreen = () => {
variant="subtitle1"
color="red500"
textAlign="center"
marginTop="m"
marginVertical="l"
marginHorizontal="xl"
>
{t('accountSetup.passphrase.subtitle2')}
</Text>
<Box height={{ smallPhone: 80, phone: 100 }} marginTop="l">
<Carousel
layout="default"
vertical={false}
data={secureAccount?.mnemonic || []}
renderItem={renderItem}
sliderWidth={wp(100)}
itemWidth={wp(90)}
inactiveSlideScale={1}
onScrollIndexChanged={onSnapToItem}
useExperimentalSnap
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore this is a new beta prop and enforces only scrolling one item at a time
disableIntervalMomentum
/>
</Box>
<Pagination
dotsLength={secureAccount?.mnemonic.length || 0}
activeDotIndex={wordIndex}
dotStyle={{
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: colors.primaryText,
}}
dotContainerStyle={{ marginHorizontal: 3 }}
inactiveDotOpacity={0.4}
inactiveDotScale={1}
/>
</Box>
<Box opacity={disabled ? 0 : 100}>
<ButtonPressable
disabled={disabled}
marginHorizontal="xl"
marginBottom="l"
bottom={bottom}
borderRadius="round"
borderBottomRightRadius="round"
backgroundColor="white"
fontWeight="500"
backgroundColorOpacityPressed={0.7}
titleColor="black900"
onPress={navNext}
title={t('accountSetup.passphrase.next')}
/>
</Box>
</>
)
}, [colors.primaryText, navToTop, t])

return (
<Box flex={1} backgroundColor="secondaryBackground">
<RevealWords
ListHeaderComponent={ListHeaderComponent}
mnemonic={secureAccount?.mnemonic || []}
onDone={navNext}
/>
</Box>
)
}
Expand Down
Loading

0 comments on commit 1d92d7b

Please sign in to comment.