Skip to content

Commit

Permalink
Connection Screens Revamp Follow-up (#780)
Browse files Browse the repository at this point in the history
authored-by: Mahmoud Aboelenein <[email protected]>

* resolve ui issues

* missing stx-address call out
---------
  • Loading branch information
m-aboelenein authored Jan 26, 2024
1 parent 410001a commit 7ae43a5
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 80 deletions.
85 changes: 54 additions & 31 deletions src/app/screens/connect/authenticationRequest/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import { MESSAGE_SOURCE } from '@common/types/message-types';
import { delay } from '@common/utils/ledger';
import BottomModal from '@components/bottomModal';
import ActionButton from '@components/button';
import InfoContainer from '@components/infoContainer';
import LedgerConnectionView from '@components/ledger/connectLedgerView';
import useSeedVault from '@hooks/useSeedVault';
import useWalletSelector from '@hooks/useWalletSelector';
import Transport from '@ledgerhq/hw-transport-webusb';
import { animated, useSpring } from '@react-spring/web';
import SelectAccount from '@screens/connect/selectAccount';
import {
AuthRequest,
createAuthResponse,
handleLedgerStxJWTAuth,
} from '@secretkeylabs/xverse-core';
import { AddressVersion, StacksMessageType, publicKeyToAddress } from '@stacks/transactions';
import Callout from '@ui-library/callout';
import { StickyHorizontalSplitButtonContainer } from '@ui-library/common.styled';
import { isHardwareAccount } from '@utils/helper';
import { decodeToken } from 'jsontokens';
Expand All @@ -30,9 +31,10 @@ import validUrl from 'valid-url';
import AddressPurposeBox from '../addressPurposeBox';
import PermissionsList from '../permissionsList';

const MainContainer = styled.div((props) => ({
const MainContainer = styled(animated.div)((props) => ({
display: 'flex',
flexDirection: 'column',
flex: 1,
paddingLeft: props.theme.space.m,
paddingRight: props.theme.space.m,
...props.theme.scrollbar,
Expand Down Expand Up @@ -76,8 +78,8 @@ const DappName = styled.h2((props) => ({
}));

const InfoContainerWrapper = styled.div((props) => ({
margin: props.theme.spacing(10),
marginBottom: 0,
marginTop: props.theme.spacing(10),
marginBottom: 'auto',
}));

const AddressesContainer = styled.div((props) => ({
Expand Down Expand Up @@ -109,6 +111,17 @@ function AuthenticationRequest() {
const { getSeed } = useSeedVault();
const isDisabled = !selectedAccount?.stxAddress;

const styles = useSpring({
from: {
opacity: 0,
y: 24,
},
to: {
y: 0,
opacity: 1,
},
});

const confirmCallback = async () => {
setLoading(true);
try {
Expand Down Expand Up @@ -243,42 +256,52 @@ function AuthenticationRequest() {
};

return (
<MainContainer>
<MainContainer style={styles}>
{getDappLogo()}
<Title>{t('TITLE')}</Title>
<DappName>{`${t('REQUEST_TOOLTIP')} ${authRequest.payload.appDetails?.name}`}</DappName>
<SelectAccount account={selectedAccount!} handlePressAccount={handleSwitchAccount} />
<AddressesContainer>
<AddressPurposeBox
purpose={AddressPurpose.Stacks}
icon={stxIcon}
title={t('STX_ADDRESS')}
address={selectedAccount?.stxAddress || stxAddress}
bnsName={selectedAccount?.bnsName}
/>
<AddressPurposeBox
purpose={AddressPurpose.Payment}
icon={BitcoinIcon}
title={t('BITCOIN_ADDRESS')}
address={selectedAccount?.btcAddress || btcAddress}
/>
</AddressesContainer>
<PermissionsContainer>
<PermissionsList />
</PermissionsContainer>
<StickyHorizontalSplitButtonContainer>
<ActionButton text={t('CANCEL_BUTTON')} transparent onPress={cancelCallback} />
<ActionButton text={t('CONNECT_BUTTON')} processing={loading} onPress={confirmCallback} />
</StickyHorizontalSplitButtonContainer>
{isDisabled && (
{!isDisabled ? (
<>
<AddressesContainer>
<AddressPurposeBox
purpose={AddressPurpose.Stacks}
icon={stxIcon}
title={t('STX_ADDRESS')}
address={selectedAccount?.stxAddress || stxAddress}
bnsName={selectedAccount?.bnsName}
/>
<AddressPurposeBox
purpose={AddressPurpose.Payment}
icon={BitcoinIcon}
title={t('BITCOIN_ADDRESS')}
address={selectedAccount?.btcAddress || btcAddress}
/>
</AddressesContainer>
<PermissionsContainer>
<PermissionsList />
</PermissionsContainer>
</>
) : (
<InfoContainerWrapper>
<InfoContainer
<Callout
bodyText={t('NO_STACKS_AUTH_SUPPORT.TITLE')}
redirectText={t('NO_STACKS_AUTH_SUPPORT.LINK')}
onClick={handleAddStxLedgerAccount}
onClickRedirect={handleAddStxLedgerAccount}
/>
</InfoContainerWrapper>
)}

<StickyHorizontalSplitButtonContainer>
<ActionButton text={t('CANCEL_BUTTON')} transparent onPress={cancelCallback} />
<ActionButton
text={t('CONNECT_BUTTON')}
processing={loading}
onPress={confirmCallback}
disabled={isDisabled}
/>
</StickyHorizontalSplitButtonContainer>

<BottomModal header="" visible={isModalVisible} onClose={() => setIsModalVisible(false)}>
{currentStepIndex === 0 && (
<LedgerConnectionView
Expand Down Expand Up @@ -306,7 +329,7 @@ function AuthenticationRequest() {
<ActionButton
onPress={isTxRejected || isConnectFailed ? handleRetry : handleConnectAndConfirm}
text={t(isTxRejected || isConnectFailed ? 'LEDGER.RETRY_BUTTON' : 'CONNECT_BUTTON')}
disabled={isButtonDisabled}
disabled={isButtonDisabled || isDisabled}
processing={isButtonDisabled}
/>
<ActionButton onPress={cancelCallback} text={t('CANCEL_BUTTON')} transparent />
Expand Down
76 changes: 48 additions & 28 deletions src/app/screens/connect/btcSelectAddressScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import OrdinalsIcon from '@assets/img/nftDashboard/white_ordinals_icon.svg';
import ActionButton from '@components/button';
import useBtcAddressRequest from '@hooks/useBtcAddressRequest';
import useWalletSelector from '@hooks/useWalletSelector';
import { animated, useSpring } from '@react-spring/web';
import { animated, useTransition } from '@react-spring/web';
import SelectAccount from '@screens/connect/selectAccount';
import { StickyHorizontalSplitButtonContainer } from '@ui-library/common.styled';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { MoonLoader } from 'react-spinners';
import { AddressPurpose } from 'sats-connect';
import styled from 'styled-components';
import AddressPurposeBox from '../addressPurposeBox';
import PermissionsList from '../permissionsList';
import { getAppIconFromWebManifest } from './helper';

const OuterContainer = styled(animated.div)((props) => ({
const OuterContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
paddingLeft: props.theme.space.m,
Expand Down Expand Up @@ -71,25 +72,28 @@ const PermissionsContainer = styled.div((props) => ({
paddingBottom: props.theme.space.xxl,
}));

const LoaderContainer = styled.div(() => ({
justifySelf: 'center',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flex: 1,
}));

function BtcSelectAddressScreen() {
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' });
const { network, btcAddress, ordinalsAddress, stxAddress, selectedAccount } = useWalletSelector();
const [appIcon, setAppIcon] = useState<string>('');
const [isLoadingIcon, setIsLoadingIcon] = useState(false);
const { payload, origin, approveBtcAddressRequest, cancelAddressRequest } =
useBtcAddressRequest();
const appUrl = useMemo(() => origin.replace(/(^\w+:|^)\/\//, ''), [origin]);

const styles = useSpring({
from: {
opacity: 0,
y: 24,
},
to: {
y: 0,
opacity: 1,
},
const transition = useTransition(isLoadingIcon, {
from: { opacity: 0, y: 30 },
enter: { opacity: 1, y: 0 },
});

const confirmCallback = async () => {
Expand Down Expand Up @@ -139,8 +143,10 @@ function BtcSelectAddressScreen() {
useEffect(() => {
(async () => {
if (origin !== '') {
setIsLoadingIcon(true);
getAppIconFromWebManifest(origin).then((appIcons) => {
setAppIcon(appIcons);
setIsLoadingIcon(false);
});
}
})();
Expand Down Expand Up @@ -188,23 +194,37 @@ function BtcSelectAddressScreen() {
navigate('/account-list?hideListActions=true');
};

return (
<OuterContainer style={styles}>
<HeadingContainer>
{appIcon !== '' ? <TopImage src={appIcon} alt="Dapp Logo" /> : null}
<Title>{t('TITLE')}</Title>
<DapURL>{appUrl}</DapURL>
</HeadingContainer>
{payload.message ? <RequestMessage>{payload.message.substring(0, 80)}</RequestMessage> : null}
<SelectAccount account={selectedAccount!} handlePressAccount={handleSwitchAccount} />
<AddressBoxContainer>{payload.purposes.map(AddressPurposeRow)}</AddressBoxContainer>
<PermissionsContainer>
<PermissionsList />
</PermissionsContainer>
<StickyHorizontalSplitButtonContainer>
<ActionButton text={t('CANCEL_BUTTON')} transparent onPress={cancelCallback} />
<ActionButton text={t('CONNECT_BUTTON')} processing={loading} onPress={confirmCallback} />
</StickyHorizontalSplitButtonContainer>
return isLoadingIcon ? (
<LoaderContainer>
<MoonLoader color="white" size={50} />
</LoaderContainer>
) : (
<OuterContainer>
{transition((style) => (
<animated.div style={style}>
<HeadingContainer>
{appIcon !== '' ? <TopImage src={appIcon} alt="Dapp Logo" /> : null}
<Title>{t('TITLE')}</Title>
<DapURL>{appUrl}</DapURL>
</HeadingContainer>
{payload.message ? (
<RequestMessage>{payload.message.substring(0, 80)}</RequestMessage>
) : null}
<SelectAccount account={selectedAccount!} handlePressAccount={handleSwitchAccount} />
<AddressBoxContainer>{payload.purposes.map(AddressPurposeRow)}</AddressBoxContainer>
<PermissionsContainer>
<PermissionsList />
</PermissionsContainer>
<StickyHorizontalSplitButtonContainer>
<ActionButton text={t('CANCEL_BUTTON')} transparent onPress={cancelCallback} />
<ActionButton
text={t('CONNECT_BUTTON')}
processing={loading}
onPress={confirmCallback}
/>
</StickyHorizontalSplitButtonContainer>
</animated.div>
))}
</OuterContainer>
);
}
Expand Down
52 changes: 33 additions & 19 deletions src/app/screens/connect/selectAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ const AccountInfoContainer = styled.button((props) => ({
padding: props.theme.space.m,
}));

const CurrentAccountContainer = styled.div((props) => ({
const CurrentAccountContainer = styled.div({
display: 'flex',
alignItems: 'center',
}));
});

const CurrentAccountTextContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: props.theme.spacing(4),
paddingLeft: props.theme.spacing(4),
paddingLeft: props.theme.space.xs,
}));

const CurrentSelectedAccountText = styled.h1((props) => ({
Expand All @@ -48,6 +48,11 @@ const GradientCircle = styled.div<GradientCircleProps>((props) => ({
background: `linear-gradient(to bottom,${props.firstGradient}, ${props.secondGradient},${props.thirdGradient} )`,
}));

const AccountTag = styled.div(() => ({
display: 'flex',
alignItems: 'center',
}));

const SwitchAccountContainer = styled.div(() => ({
display: 'flex',
alignItems: 'center',
Expand All @@ -66,28 +71,37 @@ type SelectAccountProps = {

function SelectAccount({ account, handlePressAccount }: SelectAccountProps) {
const gradient = getAccountGradient(account?.stxAddress || account?.btcAddress!);
// const { t } = useTranslation('translation', { keyPrefix: 'DASHBOARD_SCREEN' });
const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' });
const theme = useTheme();
const getName = () =>
account?.accountName ??
account?.bnsName ??
`${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}`;
const getName = () => {
const maxNameCharacters = isHardwareAccount(account) || account.bnsName ? 12 : 20;
const maxLength =
account?.accountName && account?.accountName?.length > maxNameCharacters ? '...' : '';
if (account.accountName) {
return `${account?.accountName?.slice(0, maxNameCharacters)}${maxLength}`;
}
if (account.bnsName) {
return `${account.bnsName.slice(0, maxNameCharacters)}${maxLength}`;
}
return `${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}`;
};

return (
<AccountInfoContainer onClick={handlePressAccount}>
<CurrentAccountContainer>
<GradientCircle
firstGradient={gradient[0]}
secondGradient={gradient[1]}
thirdGradient={gradient[2]}
/>
{account && (
<CurrentAccountTextContainer>
<CurrentSelectedAccountText>{getName()}</CurrentSelectedAccountText>
{isHardwareAccount(account) && <img src={LedgerBadge} alt="Ledger icon" />}
</CurrentAccountTextContainer>
)}
<AccountTag>
<GradientCircle
firstGradient={gradient[0]}
secondGradient={gradient[1]}
thirdGradient={gradient[2]}
/>
{account && (
<CurrentAccountTextContainer>
<CurrentSelectedAccountText>{getName()}</CurrentSelectedAccountText>
{isHardwareAccount(account) && <img src={LedgerBadge} alt="Ledger icon" />}
</CurrentAccountTextContainer>
)}
</AccountTag>
</CurrentAccountContainer>
<SwitchAccountContainer>
<SwitchAccountText>{t('CHANGE_ACCOUNT_BUTTON')}</SwitchAccountText>
Expand Down
4 changes: 2 additions & 2 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@
"PERMISSION_REQUEST_TX": "Request transaction signing",
"NO_STACKS_AUTH_SUPPORT": {
"TITLE": "This wallet does not have a Stacks address.",
"LINK": "Create an address here"
"LINK": "Create a Stacks address"
},
"LEDGER": {
"CONNECT": {
Expand Down Expand Up @@ -1050,7 +1050,7 @@
"BITCOIN_ADDRESS": "Payments address",
"ORDINAL_ADDRESS": "Ordinals address",
"STX_ADDRESS": "Stacks address",
"CHANGE_ACCOUNT_BUTTON": "Change account",
"CHANGE_ACCOUNT_BUTTON": "Select",
"NETWORK_MISMATCH_ERROR_TITLE": "Mismatched Network",
"NETWORK_MISMATCH_ERROR_DESCRIPTION": "The app is requesting your wallet address for a different network. You may have to switch your active network in wallet settings.",
"INVALID_PURPOSE_ERROR_TITLE": "Invalid Request",
Expand Down

0 comments on commit 7ae43a5

Please sign in to comment.