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

chore: release v1.774.0 #8881

Merged
merged 6 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .env.base
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ REACT_APP_FEATURE_NEW_WALLET_FLOW=true
REACT_APP_FEATURE_FOX_PAGE_FOX_WIF_HAT_SECTION=true
REACT_APP_FEATURE_NEW_LIMIT_FLOW=true
REACT_APP_FEATURE_THORCHAIN_SWAPPER_ACK=false
REACT_APP_FEATURE_THORCHAIN_POOLS_INSTABILITY_WARNINGS=true

# absolute URL prefix
REACT_APP_ABSOLUTE_URL_PREFIX=https://app.shapeshift.com
Expand Down
3 changes: 1 addition & 2 deletions src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,6 @@
"poolValue": "Pool Value",
"yearn": "Yearn Finance",
"totalValue": "Total Value",
"byAsset": "By Asset",
"byProvider": "By Provider",
"claimableRewards": "Claimable Rewards",
"stakingPosition": "Staking Position",
"liquidityPool": "Liquidity Pool",
Expand Down Expand Up @@ -2565,6 +2563,7 @@
"unsupportedDemoWalletExplainer": "The demo wallet is not supported, please connect to a wallet to use this feature.",
"unsupportedNetworkExplainer": "Your wallet isn't connected to %{network}. Please connect this chain or switch to a wallet that supports this network.",
"unsupportedNetworksExplainer": "Your wallet isn't connected to %{network1} and %{network2}. Please connect this chain or switch to a wallet that supports these networks.",
"instabilityWarning": "Users are currently experiencing refunds while depositing to THORChain pools. Please proceed with caution.",
"shareOfPool": "Share of Pool",
"assetDepositAmount": "%{asset} Deposit Amount",
"totalLiquidity": "Total Liquidity",
Expand Down
98 changes: 98 additions & 0 deletions src/components/FileUpload/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Box, FormControl, Icon, Input, Text as CText } from '@chakra-ui/react'
import { useColorModeValue } from '@chakra-ui/system'
import { useCallback, useState } from 'react'
import { FaFile } from 'react-icons/fa'
import { Text } from 'components/Text'

const hoverSx = { borderColor: 'blue.500' }

// TODO(gomes): use https://www.chakra-ui.com/docs/components/file-upload if/when we migrate to chakra@3
export const FileUpload = ({ onFileSelect }: { onFileSelect: (file: File) => void }) => {
const borderColor = useColorModeValue('gray.200', 'gray.600')
const [isDragging, setIsDragging] = useState(false)
const [filename, setFilename] = useState<string | null>(null)

const handleDragEnter = useCallback((e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
setIsDragging(true)
}, [])

const handleDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
setIsDragging(false)
}, [])

const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
}, [])

const processFile = useCallback(
(file: File) => {
setFilename(file.name)
onFileSelect(file)
},
[onFileSelect],
)

const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
setIsDragging(false)

const files = e.dataTransfer.files
if (files?.[0]) {
processFile(files[0])
}
},
[processFile],
)

const handleFileInput = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files
if (files?.[0]) {
processFile(files[0])
}
},
[processFile],
)

return (
<FormControl>
<Input type='file' accept='.txt' onChange={handleFileInput} id='file-upload' display='none' />
<Box
as='label'
htmlFor='file-upload'
w='full'
h='32'
border='2px'
borderStyle='dashed'
borderColor={isDragging ? 'blue.500' : borderColor}
borderRadius='xl'
display='flex'
flexDirection='column'
alignItems='center'
justifyContent='center'
bg='background.surface.raised.base'
cursor='pointer'
transition='all 0.2s'
_hover={hoverSx}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<Icon as={FaFile} boxSize={6} color='gray.500' mb={2} />
{filename ? (
<CText color='gray.500'>{filename}</CText>
) : (
<Text color='gray.500' translation='walletProvider.shapeShift.import.dragAndDrop' />
)}
</Box>
</FormControl>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const makeOrdinalSuffix = (n: number) => {
return ['st', 'nd', 'rd'][((((n + 90) % 100) - 10) % 10) - 1] || 'th'
}

const TEST_COUNT_REQUIRED = 4
const TEST_COUNT_REQUIRED = 3

export const CreateBackupConfirm = () => {
const history = useHistory()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import {
Box,
Button,
FormControl,
FormErrorMessage,
Input,
Text as CText,
VStack,
} from '@chakra-ui/react'
import { useCallback, useState } from 'react'
import type { FieldValues } from 'react-hook-form'
import { useForm } from 'react-hook-form'
import { useTranslate } from 'react-polyglot'
import { useHistory } from 'react-router-dom'
import { FileUpload } from 'components/FileUpload/FileUpload'
import { MobileWalletDialogRoutes } from 'components/MobileWalletDialog/types'
import { decryptFromKeystore } from 'components/MobileWalletDialog/utils'
import { DialogBackButton } from 'components/Modal/components/DialogBackButton'
import { DialogBody } from 'components/Modal/components/DialogBody'
import { DialogCloseButton } from 'components/Modal/components/DialogCloseButton'
import { DialogFooter } from 'components/Modal/components/DialogFooter'
import {
DialogHeader,
DialogHeaderLeft,
DialogHeaderRight,
} from 'components/Modal/components/DialogHeader'
import { SlideTransition } from 'components/SlideTransition'
import { Text } from 'components/Text'
import { addWallet } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers'
import type { NativeWalletValues } from 'context/WalletProvider/NativeWallet/types'
import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton'
import { MixPanelEvent } from 'lib/mixpanel/types'

export const ImportKeystore = () => {
const history = useHistory()
const [keystoreFile, setKeystoreFile] = useState<string | null>(null)
const mixpanel = getMixPanel()

const translate = useTranslate()

const {
setError,
handleSubmit,
formState: { errors, isSubmitting, isValid },
register,
} = useForm<NativeWalletValues>({
mode: 'onChange',
shouldUnregister: true,
})

const onSubmit = useCallback(
async (values: FieldValues) => {
if (!keystoreFile) {
throw new Error('No keystore uploaded')
}
const parsedKeystore = JSON.parse(keystoreFile)

try {
const mnemonic = await decryptFromKeystore(parsedKeystore, values.keystorePassword)

const revocableVault = await addWallet({
mnemonic,
label: values.name.trim(),
})
history.push(MobileWalletDialogRoutes.ImportSuccess, { vault: revocableVault })
mixpanel?.track(MixPanelEvent.NativeImportKeystore)
} catch (e) {
setError('keystorePassword', {
type: 'manual',
message: translate('walletProvider.shapeShift.import.invalidKeystorePassword'),
})
}
},
[history, keystoreFile, mixpanel, setError, translate],
)

const handleFileSelect = useCallback((file: File) => {
const reader = new FileReader()
reader.onload = e => {
if (!e?.target) return
if (typeof e.target.result !== 'string') return
setKeystoreFile(e.target.result)
}
reader.readAsText(file)
}, [])

const handleGoBack = useCallback(() => {
history.goBack()
}, [history])

return (
<SlideTransition>
<DialogHeader>
<DialogHeaderLeft>
<DialogBackButton onClick={handleGoBack} />
</DialogHeaderLeft>
<DialogHeaderRight>
<DialogCloseButton />
</DialogHeaderRight>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)}>
<DialogBody>
<VStack spacing={2} mb={6} alignItems='flex-start'>
<Box>
<CText fontSize='2xl' fontWeight='bold' mb={0}>
<Text translation={'walletProvider.shapeShift.import.keystoreHeader'} />
</CText>
<CText color='text.subtle' mb={6}>
<Text translation='walletProvider.shapeShift.import.keystoreImportBody' />
</CText>
</Box>
<VStack spacing={6} width='full'>
<FormControl isInvalid={Boolean(errors.name)}>
<Input
size='lg'
variant='filled'
placeholder={translate('walletProvider.create.walletName')}
{...register('name', {
required: true,
maxLength: {
value: 64,
message: translate('modals.password.error.maxLength', {
length: 64,
}),
},
})}
/>
<FormErrorMessage>{errors.name?.message}</FormErrorMessage>
</FormControl>
<FileUpload onFileSelect={handleFileSelect} />
<FormControl isInvalid={Boolean(errors.keystorePassword)}>
<Input
type='password'
placeholder='Keystore Password'
size='lg'
variant='filled'
data-test='wallet-native-keystore-password'
{...register('keystorePassword')}
/>
<FormErrorMessage>{errors.keystorePassword?.message}</FormErrorMessage>
</FormControl>
</VStack>
</VStack>
</DialogBody>
<DialogFooter>
{keystoreFile && (
<Button
colorScheme='blue'
width='full'
size='lg'
type='submit'
isLoading={isSubmitting}
isDisabled={!isValid}
data-test='wallet-native-keystore-submit'
>
<Text translation='walletProvider.shapeShift.import.importKeystore' />
</Button>
)}
</DialogFooter>
</form>
</SlideTransition>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { MemoryRouter, Redirect, Route, Switch, useHistory } from 'react-router'
import { MobileWalletDialogRoutes } from 'components/MobileWalletDialog/types'
import { SlideTransition } from 'components/SlideTransition'

import { ImportKeystore } from './ImportKeystore'
import { ImportSeedPhrase } from './ImportSeedPhrase'
import { ImportSuccess } from './ImportSuccess'
import { ImportWallet } from './ImportWallet'

Expand All @@ -29,6 +31,12 @@ export const ImportRouter = ({ onClose, defaultRoute }: ImportRouterProps) => {
<Route path={MobileWalletDialogRoutes.ImportSuccess}>
<ImportSuccess onClose={onClose} />
</Route>
<Route path={MobileWalletDialogRoutes.ImportSeedPhrase}>
<ImportSeedPhrase />
</Route>
<Route path={MobileWalletDialogRoutes.ImportKeystore}>
<ImportKeystore />
</Route>
<Route path={MobileWalletDialogRoutes.Import}>
<ImportWallet
isDefaultRoute={defaultRoute === MobileWalletDialogRoutes.Import}
Expand Down
Loading
Loading