Skip to content

Commit

Permalink
Merge pull request #1051 from alephium/ledger-improvements
Browse files Browse the repository at this point in the history
Ledger UX improvements
  • Loading branch information
nop33 authored Dec 16, 2024
2 parents 5c34045 + 763854f commit 5ff454d
Show file tree
Hide file tree
Showing 18 changed files with 244 additions and 16 deletions.
7 changes: 6 additions & 1 deletion apps/desktop-wallet/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,5 +506,10 @@
"Signing messages with Ledger is not supported.": "Signing messages with Ledger is not supported.",
"Plug in and unlock your Ledger device.": "Plug in and unlock your Ledger device.",
"ledgerInstructionsOpenApp": "Open the Alephium Ledger app. The Alephium app can be installed via <1>Ledger Live</1>.",
"Is your device plugged in and the Alephium app open?": "Is your device plugged in and the Alephium app open?"
"Is your device plugged in and the Alephium app open?": "Is your device plugged in and the Alephium app open?",
"Please, confirm the transaction on your Ledger.": "Please, confirm the transaction on your Ledger.",
"Welcome to your Ledger!": "Welcome to your Ledger!",
"Would you like to scan for active addresses?": "Would you like to scan for active addresses?",
"Scan": "Scan",
"Active address discovery completed. Addresses added: {{ count }}": "Active address discovery completed. Addresses added: {{ count }}"
}
2 changes: 1 addition & 1 deletion apps/desktop-wallet/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "alephium-desktop-wallet",
"description": "The official Alephium wallet",
"version": "2.5.0-rc.1",
"version": "2.5.0-rc.2",
"author": "Alephium dev <dev@alephium.org>",
"main": "dist-electron/main.js",
"homepage": "./",
Expand Down
5 changes: 4 additions & 1 deletion apps/desktop-wallet/src/components/SnackbarManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { memo, useEffect } from 'react'
import styled from 'styled-components'

import { fadeInBottom, fadeOut } from '@/animations'
import LedgerAddressDiscoverySnackbar from '@/features/ledger/LedgerAddressDiscoverySnackbar'
import SentTransactionSnackbarPopup from '@/features/send/sentTransactions/SentTransactionSnackbarPopup'
import { selectAllSentTransactions } from '@/features/send/sentTransactions/sentTransactionsSelectors'
import SnackbarBox from '@/features/snackbar/SnackbarBox'
Expand All @@ -32,17 +33,19 @@ import { SnackbarMessage } from '@/types/snackbar'
const SnackbarManager = () => {
const messages = useAppSelector((state) => state.snackbar.messages)
const sentTxs = useAppSelector(selectAllSentTransactions)
const isNewLedgerDevice = useAppSelector((s) => s.ledger.isNewDevice)

return (
<ModalPortal>
{(messages.length > 0 || sentTxs.length > 0) && (
{(messages.length > 0 || sentTxs.length > 0 || isNewLedgerDevice) && (
<SnackbarManagerContainer>
{sentTxs.map((sentTxs) => (
<SentTransactionSnackbarPopup key={sentTxs.hash} txHash={sentTxs.hash} />
))}
{messages.map((message) => (
<SnackbarPopup key={message.id} message={message} />
))}
{isNewLedgerDevice && <LedgerAddressDiscoverySnackbar />}
</SnackbarManagerContainer>
)}
</ModalPortal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'

import Button from '@/components/Button'
import { ReactComponent as LedgerLogo } from '@/images/ledger.svg'

const ConnectWithLedgerButton = () => {
const { t } = useTranslation()
Expand All @@ -29,7 +30,12 @@ const ConnectWithLedgerButton = () => {
navigate('/ledger')
}

return <Button onClick={handleLedgerConnectClick}>{t('Connect with Ledger')}</Button>
return (
<Button onClick={handleLedgerConnectClick} role="secondary" variant="faded" short style={{ width: '80%' }}>
<LedgerLogo style={{ width: '15px', marginRight: '10px' }} />
{t('Connect with Ledger')}
</Button>
)
}

export default ConnectWithLedgerButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2018 - 2024 The Alephium Authors
This file is part of the alephium project.
The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { X } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

import { fadeInBottom, fadeOut } from '@/animations'
import Button from '@/components/Button'
import { userWasAskedToDiscoverAddresses } from '@/features/ledger/ledgerActions'
import SnackbarBox from '@/features/snackbar/SnackbarBox'
import { useAppDispatch } from '@/hooks/redux'
import useAddressGeneration from '@/hooks/useAddressGeneration'

const LedgerAddressDiscoverySnackbar = () => {
const dispatch = useAppDispatch()
const { t } = useTranslation()
const { discoverAndSaveUsedAddresses } = useAddressGeneration()

const handleCloseClick = () => {
dispatch(userWasAskedToDiscoverAddresses())
}

const handleScanClick = () => {
discoverAndSaveUsedAddresses()
dispatch(userWasAskedToDiscoverAddresses())
}

return (
<SentTransactionSnackbarPopupStyled {...fadeInBottom} {...fadeOut} className="info">
<Columns>
<Messages>
<Message>{t('Welcome to your Ledger!') + ' 👋'}</Message>
<div>{t('Would you like to scan for active addresses?')}</div>
</Messages>
<Button short borderless role="secondary" onClick={handleScanClick}>
{t('Scan')}
</Button>
<Button aria-label={t('Close')} squared role="secondary" onClick={handleCloseClick} borderless transparent>
<X />
</Button>
</Columns>
</SentTransactionSnackbarPopupStyled>
)
}

export default LedgerAddressDiscoverySnackbar

const SentTransactionSnackbarPopupStyled = styled(SnackbarBox)`
display: flex;
gap: 20px;
min-width: 400px;
`

const Columns = styled.div`
flex: 1;
display: flex;
gap: 30px;
align-items: center;
`

const Message = styled.span`
font-weight: bold;
`

const Messages = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
`
23 changes: 23 additions & 0 deletions apps/desktop-wallet/src/features/ledger/ledgerActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Copyright 2018 - 2024 The Alephium Authors
This file is part of the alephium project.
The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { createAction } from '@reduxjs/toolkit'

export const newLedgerDeviceConnected = createAction('ledger/newLedgerDeviceConnected')

export const userWasAskedToDiscoverAddresses = createAction('ledger/userWasAskedToDiscoverAddresses')
46 changes: 46 additions & 0 deletions apps/desktop-wallet/src/features/ledger/ledgerSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright 2018 - 2024 The Alephium Authors
This file is part of the alephium project.
The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { createSlice } from '@reduxjs/toolkit'

import { newLedgerDeviceConnected, userWasAskedToDiscoverAddresses } from './ledgerActions'

interface LedgerState {
isNewDevice: boolean
}

const initialState: LedgerState = {
isNewDevice: false
}

const ledgerSlice = createSlice({
name: 'ledger',
initialState,
reducers: {},
extraReducers(builder) {
builder
.addCase(newLedgerDeviceConnected, (state) => {
state.isNewDevice = true
})
.addCase(userWasAskedToDiscoverAddresses, (state) => {
state.isNewDevice = false
})
}
})

export default ledgerSlice
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import { NonSensitiveAddressData } from '@alephium/keyring'
import { useNavigate } from 'react-router-dom'

import { usePersistQueryClientContext } from '@/api/persistQueryClientContext'
import { newLedgerDeviceConnected } from '@/features/ledger/ledgerActions'
import { generateUuidFromInitialAddress } from '@/features/ledger/utils'
import { useAppDispatch } from '@/hooks/redux'
import useAddressGeneration from '@/hooks/useAddressGeneration'
import { addressMetadataStorage } from '@/storage/addresses/addressMetadataPersistentStorage'
import { walletUnlocked } from '@/storage/wallets/walletActions'

const useInitializeAppWithLedgerData = () => {
Expand All @@ -36,6 +38,10 @@ const useInitializeAppWithLedgerData = () => {

const walletId = await generateUuidFromInitialAddress(initialAddress.hash)

if (addressMetadataStorage.load(walletId).length === 0) {
dispatch(newLedgerDeviceConnected())
}

await restoreQueryCache(walletId)
await restoreAddressesFromMetadata({ walletId, isPassphraseUsed: false, isLedger: true })

Expand Down
4 changes: 2 additions & 2 deletions apps/desktop-wallet/src/features/send/SendModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function SendModal<PT extends { fromAddress: Address }>({

const [addressesData, setAddressesData] = useState<AddressesTxModalData>(txData ?? initialTxData)
const [transactionData, setTransactionData] = useState(txData)
const [isLoading, setIsLoading] = useState(false)
const [isLoading, setIsLoading] = useState<boolean | string>(false)
const [step, setStep] = useState<Step>('addresses')
const [consolidationRequired, setConsolidationRequired] = useState(false)
const [isSweeping, setIsSweeping] = useState(false)
Expand Down Expand Up @@ -147,7 +147,7 @@ function SendModal<PT extends { fromAddress: Address }>({
const handleSendExtended = useCallback(async () => {
if (!transactionData) return

setIsLoading(true)
setIsLoading(isLedger ? t('Please, confirm the transaction on your Ledger.') : true)

try {
const signature =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const TokenBadgesListCell = ({ tx, refAddressHash, compact }: TransactionRowSect
export default TokenBadgesListCell

const TokenBadgesListCellStyled = styled.div<Pick<TransactionRowProps, 'compact'>>`
flex-grow: 1;
flex-shrink: 0;
width: ${({ compact }) => (compact ? '80px' : '180px')};
margin-right: var(--spacing-4);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ const DirectionalAddresses = styled.div<{ stackVertically?: boolean }>`
display: flex;
align-items: center;
min-width: 35%;
flex: 1;
justify-content: flex-end;
${({ stackVertically }) =>
stackVertically &&
Expand Down
7 changes: 7 additions & 0 deletions apps/desktop-wallet/src/hooks/useAddressGeneration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ const useAddressGeneration = () => {

try {
saveNewAddresses(newAddresses)
dispatch(
showToast({
text: t('Active address discovery completed. Addresses added: {{ count }}', { count: newAddresses.length }),
type: 'info',
duration: 'long'
})
)
} catch {
sendAnalytics({ type: 'error', message: 'Error while saving newly discovered address' })
}
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop-wallet/src/images/ledger.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 9 additions & 2 deletions apps/desktop-wallet/src/modals/CenteredModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import ModalContainer, { ModalBackdrop, ModalContainerProps } from '@/modals/Mod
export interface CenteredModalProps extends ModalContainerProps {
title?: ReactNode
subtitle?: string
isLoading?: boolean
isLoading?: boolean | string
header?: ReactNode
transparentHeader?: boolean
narrow?: boolean
Expand Down Expand Up @@ -118,9 +118,10 @@ const CenteredModal: FC<CenteredModalProps> = ({

{isLoading && (
<>
<ModalBackdrop light />
<ModalBackdrop light blur />
<ModalLoadingSpinner>
<Spinner />
{typeof isLoading === 'string' && <ModalLoadingText>{isLoading}</ModalLoadingText>}
</ModalLoadingSpinner>
</>
)}
Expand Down Expand Up @@ -252,8 +253,14 @@ const ModalLoadingSpinner = styled.div`
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
`

const IconContainer = styled.div`
margin: var(--spacing-3) 0 var(--spacing-3) var(--spacing-4);
`

const ModalLoadingText = styled.div`
font-size: 14px;
margin-top: var(--spacing-3);
`
9 changes: 7 additions & 2 deletions apps/desktop-wallet/src/modals/ModalContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.

import { motion, MotionProps } from 'framer-motion'
import { KeyboardEvent, ReactNode, useEffect, useRef } from 'react'
import styled from 'styled-components'
import styled, { css } from 'styled-components'

import { fadeInOutFast } from '@/animations'
import { closeModal } from '@/features/modals/modalActions'
Expand Down Expand Up @@ -99,7 +99,7 @@ export default styled(ModalContainer)<{ hasPadding?: boolean }>`
}
`

export const ModalBackdrop = styled(motion.div)<{ focusMode?: boolean; light?: boolean }>`
export const ModalBackdrop = styled(motion.div)<{ focusMode?: boolean; light?: boolean; blur?: boolean }>`
position: absolute;
top: 0;
right: 0;
Expand All @@ -115,6 +115,11 @@ export const ModalBackdrop = styled(motion.div)<{ focusMode?: boolean; light?: b
: light
? 'rgba(0, 0, 0, 0.25)'
: 'rgba(0, 0, 0, 0.6)'};
${({ blur }) =>
blur &&
css`
backdrop-filter: blur(4px);
`}
`

export const useMoveFocusOnPreviousModal = () => {
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop-wallet/src/pages/HomePage/NewWalletActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import styled from 'styled-components'

import ActionLink from '@/components/ActionLink'
import Button from '@/components/Button'
import HorizontalDivider from '@/components/Dividers/HorizontalDivider'
import { Section } from '@/components/PageComponents/PageContainers'
import Paragraph from '@/components/Paragraph'
import ConnectWithLedgerButton from '@/features/ledger/ConnectWithLedgerButton'
Expand All @@ -41,8 +42,11 @@ const NewWalletActions = ({ onExistingWalletLinkClick }: NewWalletActionsProps)
</Paragraph>
<Section inList>
<Button onClick={() => navigate('/create/0')}>{t('New wallet')}</Button>

<Button onClick={() => navigate('/import/0')}>{t('Import wallet')}</Button>
<HorizontalDivider style={{ width: '60%', margin: '10px 0' }} />
<ConnectWithLedgerButton />

{onExistingWalletLinkClick && (
<ActionLinkStyled onClick={onExistingWalletLinkClick}>{t('Use an existing wallet')}</ActionLinkStyled>
)}
Expand Down
Loading

0 comments on commit 5ff454d

Please sign in to comment.