diff --git a/packages/components/src/components/Passphrase/EnterOnTrezorButton.tsx b/packages/components/src/components/Passphrase/EnterOnTrezorButton.tsx
new file mode 100644
index 000000000000..72132251fc46
--- /dev/null
+++ b/packages/components/src/components/Passphrase/EnterOnTrezorButton.tsx
@@ -0,0 +1,47 @@
+import { FormattedMessage } from 'react-intl';
+import styled from 'styled-components';
+import { Image } from '../Image/Image';
+import { DeviceModelInternal } from '@trezor/connect';
+import { Card } from '../Card/Card';
+import { Icon } from '../assets/Icon/Icon';
+import { spacingsPx } from '@trezor/theme';
+
+const Row = styled.div`
+ display: flex;
+ gap: ${spacingsPx.xs};
+ align-items: center;
+ justify-content: space-between;
+`;
+
+interface EnterOnTrezorButtonProps {
+ submit: (value: string, passphraseOnDevice?: boolean) => void;
+ isVisible: boolean;
+ value: string;
+ deviceModel?: DeviceModelInternal;
+}
+
+export const EnterOnTrezorButton = ({
+ submit,
+ isVisible,
+ value,
+ deviceModel,
+}: EnterOnTrezorButtonProps) => {
+ if (!isVisible) return null;
+
+ return (
+ submit(value, true)}
+ data-test="@passphrase/enter-on-device-button"
+ >
+
+ {deviceModel && }
+
+
+
+
+ );
+};
diff --git a/packages/components/src/components/Passphrase/PassphraseTypeCard.tsx b/packages/components/src/components/Passphrase/PassphraseTypeCard.tsx
index 53b79ae10888..b82f47f7763a 100644
--- a/packages/components/src/components/Passphrase/PassphraseTypeCard.tsx
+++ b/packages/components/src/components/Passphrase/PassphraseTypeCard.tsx
@@ -19,6 +19,9 @@ import { Checkbox } from '../form/Checkbox/Checkbox';
import { Input } from '../form/Input/Input';
import { Icon } from '../assets/Icon/Icon';
import { TooltipProps, Tooltip } from '../Tooltip/Tooltip';
+import { EnterOnTrezorButton } from './EnterOnTrezorButton';
+import { Card } from '../Card/Card';
+import { DeviceModelInternal } from '@trezor/connect';
type WalletType = 'standard' | 'hidden';
@@ -27,11 +30,12 @@ type WrapperProps = {
$singleColModal?: boolean;
};
+const Item = styled.div``;
+
const Wrapper = styled.div`
display: flex;
flex: 1;
-
- /* align-items: center; */
+ gap: ${spacingsPx.xs};
border-radius: ${borders.radii.xs};
flex-direction: column;
text-align: left;
@@ -137,23 +141,6 @@ const ActionButton = styled(Button)`
}
`;
-const OnDeviceActionButton = styled(ActionButton)`
- background: transparent;
- text-decoration: underline;
- color: ${({ theme }) => theme.textSubdued};
-
- &:first-child {
- margin-top: 0;
- }
-
- &:hover,
- &:focus,
- &:active {
- color: ${({ theme }) => theme.textSubdued};
- background: transparent;
- }
-`;
-
const Content = styled.div`
display: flex;
flex: 1;
@@ -170,6 +157,7 @@ export type PassphraseTypeCardProps = {
offerPassphraseOnDevice?: boolean;
singleColModal?: boolean;
authConfirmation?: boolean;
+ deviceModel?: DeviceModelInternal | null;
onSubmit: (value: string, passphraseOnDevice?: boolean) => void;
learnMoreTooltipOnClick?: TooltipProps['addon'];
learnMoreTooltipAppendTo?: TooltipProps['appendTo'];
@@ -277,157 +265,172 @@ export const PassphraseTypeCard = (props: PassphraseTypeCardProps) => {
}}
data-test={`@passphrase-type/${props.type}`}
>
- {!props.singleColModal && (
- // only used to show options in modal where user selects wallet type
- // single col modal such as one for creating hidden wallet shows only input and submit button
- <>
-
-
- {props.type === 'standard' ? (
-
- ) : (
-
+ -
+ {!props.singleColModal && (
+ // only used to show options in modal where user selects wallet type
+ // single col modal such as one for creating hidden wallet shows only input and submit button
+ <>
+
+
+ {props.type === 'standard' ? (
+
+ ) : (
+
+ )}
+
+
+
+ {props.type === 'hidden' ? (
+
+ }
+ addon={props.learnMoreTooltipOnClick}
+ content={
+
+ }
+ dashed
+ >
+ <>{props.title}>
+
+ ) : (
+ props.title
+ )}
+
+ {props.description}
+
+ {props.type === 'standard' && (
+
+
+
)}
-
-
-
- {props.type === 'hidden' ? (
-
+
+ {props.type === 'hidden' && }
+ >
+ )}
+
+ -
+
+ {props.type === 'hidden' && (
+ <>
+
+ {/* Show passphrase input */}
+
+
+ ) : null
}
- addon={props.learnMoreTooltipOnClick}
- content={
- {
+ if (
+ typeof ref.current?.selectionStart ===
+ 'number'
+ ) {
+ caretRef.current =
+ ref.current.selectionStart;
+ }
+ setShowPassword(!showPassword);
+ }}
+ data-test="@passphrase/show-toggle"
/>
}
- dashed
- >
- <>{props.title}>
-
- ) : (
- props.title
- )}
-
- {props.description}
-
- {props.type === 'standard' && (
-
-
-
- )}
-
- {props.type === 'hidden' && }
- >
- )}
- {props.type === 'hidden' && (
- <>
-
- {/* Show passphrase input */}
-
-
- ) : null
- }
- inputState={isTooLong ? 'error' : undefined}
- autoFocus={!isAndroid()}
- innerAddon={
- {
- if (typeof ref.current?.selectionStart === 'number') {
- caretRef.current = ref.current.selectionStart;
- }
- setShowPassword(!showPassword);
- }}
- data-test="@passphrase/show-toggle"
/>
- }
- />
-
-
- {!isTooLong && }
- >
- )}
- {props.authConfirmation && (
- // Checkbox if user fully understands what's happening when confirming empty passphrase
-
- setEnabled(!enabled)}
- isChecked={enabled}
- >
-
-
-
- )}
-
- {props.type === 'hidden' && (
-
- {/* Submit button */}
- {/* Visible in standalone modal for creating a hidden wallet or after a click also in modal for selecting wallet type */}
- {(props.singleColModal || hiddenWalletTouched) && (
-
- submit(value)}
- isFullWidth
- >
- {props.submitLabel}
-
-
- )}
- {/* Offer entering passphrase on a device */}
- {props.offerPassphraseOnDevice && (
- submit(value, true)}
- isFullWidth
- data-test="@passphrase/enter-on-device-button"
+
+
+ {!isTooLong && }
+ >
+ )}
+ {props.authConfirmation && (
+ // Checkbox if user fully understands what's happening when confirming empty passphrase
+
+ setEnabled(!enabled)}
+ isChecked={enabled}
>
-
+
+
+ )}
+
+
+ {props.type === 'hidden' && (
+
+ {/* Submit button */}
+ {/* Visible in standalone modal for creating a hidden wallet or after a click also in modal for selecting wallet type */}
+ {(props.singleColModal || hiddenWalletTouched) && (
+
+ submit(value)}
+ isFullWidth
+ >
+ {props.submitLabel}
+
+
+ )}
+
)}
-
- )}
-
+
+
+
+ -
+ {/* Offer entering passphrase on a device */}
+
+ {props.offerPassphraseOnDevice && (
+
+ )}
+
+
);
};
diff --git a/packages/components/src/components/buttons/Button/Button.stories.tsx b/packages/components/src/components/buttons/Button/Button.stories.tsx
index 791f8a9c10c4..c11b381da94b 100644
--- a/packages/components/src/components/buttons/Button/Button.stories.tsx
+++ b/packages/components/src/components/buttons/Button/Button.stories.tsx
@@ -10,5 +10,13 @@ export default meta;
export const Button: StoryObj = {
args: {
children: 'Button label',
+ margin: { top: undefined, right: undefined, bottom: undefined, left: undefined },
+ },
+ argTypes: {
+ margin: {
+ table: {
+ category: 'Frame props',
+ },
+ },
},
};
diff --git a/packages/components/src/components/buttons/Button/Button.tsx b/packages/components/src/components/buttons/Button/Button.tsx
index 9bf1d214435c..bae88af49045 100644
--- a/packages/components/src/components/buttons/Button/Button.tsx
+++ b/packages/components/src/components/buttons/Button/Button.tsx
@@ -14,15 +14,17 @@ import {
} from '../buttonStyleUtils';
import { focusStyleTransition, getFocusShadowStyle } from '../../../utils/utils';
import { useElevation } from '../../ElevationContext/ElevationContext';
+import { makePropsTransient } from '../../../utils/transientProps';
+import { FrameProps, TransientFrameProps, withFrameProps } from '../../common/frameProps';
-interface ButtonContainerProps {
+type ButtonContainerProps = TransientFrameProps & {
$variant: ButtonVariant;
$size: ButtonSize;
$iconAlignment?: IconAlignment;
$hasIcon?: boolean;
$isFullWidth?: boolean;
$elevation: Elevation;
-}
+};
export const ButtonContainer = styled.button`
display: flex;
@@ -49,6 +51,8 @@ export const ButtonContainer = styled.button`
pointer-events: none;
cursor: default;
}
+
+ ${withFrameProps}
`;
interface ContentProps {
@@ -80,20 +84,21 @@ type SelectedHTMLButtonProps = Pick<
'onClick' | 'onMouseOver' | 'onMouseLeave' | 'type' | 'tabIndex'
>;
-export interface ButtonProps extends SelectedHTMLButtonProps {
- variant?: ButtonVariant;
- size?: ButtonSize;
- isDisabled?: boolean;
- isLoading?: boolean;
- isFullWidth?: boolean;
- icon?: IconType;
- iconSize?: number;
- iconAlignment?: IconAlignment;
- children: React.ReactNode;
- title?: string;
- className?: string;
- 'data-test'?: string;
-}
+export type ButtonProps = SelectedHTMLButtonProps &
+ FrameProps & {
+ variant?: ButtonVariant;
+ size?: ButtonSize;
+ isDisabled?: boolean;
+ isLoading?: boolean;
+ isFullWidth?: boolean;
+ icon?: IconType;
+ iconSize?: number;
+ iconAlignment?: IconAlignment;
+ children: React.ReactNode;
+ title?: string;
+ className?: string;
+ 'data-test'?: string;
+ };
export const Button = ({
variant = 'primary',
@@ -106,8 +111,13 @@ export const Button = ({
iconAlignment = 'left',
type = 'button',
children,
+ margin,
...rest
}: ButtonProps) => {
+ const frameProps = {
+ margin,
+ };
+
const theme = useTheme();
const { elevation } = useElevation();
@@ -132,6 +142,7 @@ export const Button = ({
$hasIcon={!!icon || isLoading}
$elevation={elevation}
{...rest}
+ {...makePropsTransient(frameProps)}
>
{!isLoading && icon && IconComponent}
{isLoading && Loader}
diff --git a/packages/components/src/components/typography/Heading/Heading.tsx b/packages/components/src/components/typography/Heading/Heading.tsx
index de148a3cc955..4913093fddd2 100644
--- a/packages/components/src/components/typography/Heading/Heading.tsx
+++ b/packages/components/src/components/typography/Heading/Heading.tsx
@@ -1,17 +1,35 @@
import styled from 'styled-components';
-import { typography } from '@trezor/theme';
+import { TypographyStyle, typography } from '@trezor/theme';
+import { FrameProps, TransientFrameProps, withFrameProps } from '../../common/frameProps';
+import { makePropsTransient } from '../../../utils/transientProps';
-const H1 = styled.h1`
- ${typography.titleLarge};
-`;
+type HeadingProps = TransientFrameProps & {
+ children: string;
+ $typographyStyle: TypographyStyle;
+};
-const H2 = styled.h2`
- ${typography.titleMedium};
-`;
+const Heading = styled.h1`
+ ${({ $typographyStyle }) => typography[$typographyStyle]};
-const H3 = styled.h3`
- ${typography.titleSmall};
+ ${withFrameProps}
`;
-export { H1, H2, H3 };
+type HProps = FrameProps & {
+ children: React.ReactNode;
+};
+
+export const H1 = ({ margin, ...props }: HProps) => (
+
+);
+export const H2 = ({ margin, ...props }: HProps) => (
+
+);
+export const H3 = ({ margin, ...props }: HProps) => (
+
+);
diff --git a/packages/suite/src/components/suite/layouts/SuiteLayout/SuiteLayout.tsx b/packages/suite/src/components/suite/layouts/SuiteLayout/SuiteLayout.tsx
index a000c45846a1..776e8d727335 100644
--- a/packages/suite/src/components/suite/layouts/SuiteLayout/SuiteLayout.tsx
+++ b/packages/suite/src/components/suite/layouts/SuiteLayout/SuiteLayout.tsx
@@ -19,9 +19,9 @@ import { Sidebar } from './Sidebar/Sidebar';
import { CoinjoinBars } from './CoinjoinBars/CoinjoinBars';
import { MobileAccountsMenu } from 'src/components/wallet/WalletLayout/AccountsMenu/MobileAccountsMenu';
import { selectSelectedAccount } from 'src/reducers/wallet/selectedAccountReducer';
+import { useAppShortcuts } from './utils';
export const SCROLL_WRAPPER_ID = 'layout-scroll';
-
export const Wrapper = styled.div`
display: flex;
flex: 1;
@@ -100,6 +100,8 @@ export const SuiteLayout = ({ children }: SuiteLayoutProps) => {
const isAccountPage = !!selectedAccount;
+ useAppShortcuts();
+
return (
diff --git a/packages/suite/src/components/suite/layouts/SuiteLayout/utils.ts b/packages/suite/src/components/suite/layouts/SuiteLayout/utils.ts
index 0305f076eeb2..3d6c6ce5eed7 100644
--- a/packages/suite/src/components/suite/layouts/SuiteLayout/utils.ts
+++ b/packages/suite/src/components/suite/layouts/SuiteLayout/utils.ts
@@ -1,5 +1,8 @@
+import { createDeviceInstance, selectDevice } from '@suite-common/wallet-core';
+import { useEvent } from 'react-use';
+import { goto } from 'src/actions/suite/routerActions';
import { useCustomBackends } from 'src/hooks/settings/backends';
-import { useSelector } from 'src/hooks/suite';
+import { useDispatch, useSelector } from 'src/hooks/suite';
export const useEnabledBackends = () => {
const enabledNetworks = useSelector(state => state.wallet.settings.enabledNetworks);
@@ -7,3 +10,30 @@ export const useEnabledBackends = () => {
return customBackends.filter(backend => enabledNetworks.includes(backend.coin));
};
+
+export const useAppShortcuts = () => {
+ const device = useSelector(selectDevice);
+ const dispatch = useDispatch();
+
+ useEvent('keydown', e => {
+ const modKey = e.metaKey; // CMD or Ctrl key
+
+ // press CMD + P to show PassphraseModal
+ if (modKey && e.key === 'p' && device) {
+ dispatch(createDeviceInstance({ device }));
+ e.preventDefault(); // prevent default behaviour
+ }
+
+ // press CMD + D to show SwitchDevice
+ if (modKey && e.key === 'd' && device) {
+ dispatch(
+ goto('suite-switch-device', {
+ params: {
+ cancelable: true,
+ },
+ }),
+ );
+ e.preventDefault(); // prevent default behaviour
+ }
+ });
+};
diff --git a/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoader.tsx b/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoader.tsx
index 50ddb225712b..6dc2bbacd48e 100644
--- a/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoader.tsx
+++ b/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoader.tsx
@@ -1,26 +1,37 @@
import styled from 'styled-components';
-import { Spinner } from '@trezor/components';
-import { Translation, Modal } from 'src/components/suite';
+import { H3, Spinner, Text } from '@trezor/components';
+import { Translation } from 'src/components/suite';
+import { CardWithDevice } from 'src/views/suite/SwitchDevice/CardWithDevice';
+import { SwitchDeviceRenderer } from 'src/views/suite/SwitchDevice/SwitchDeviceRenderer';
+import { useSelector } from 'src/hooks/suite';
+import { selectDevice } from '@suite-common/wallet-core';
const Expand = styled.div`
display: flex;
+ flex-direction: column;
width: 100%;
justify-content: center;
+ align-items: center;
margin: 40px 0;
`;
-const StyledModal = styled(Modal)`
- width: 360px;
-`;
+export const DiscoveryLoader = () => {
+ const device = useSelector(selectDevice);
+ if (!device) return null;
-export const DiscoveryLoader = () => (
- }
- description={}
- data-test="@discovery/loader"
- >
-
-
-
-
-);
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoaderLegacy.tsx b/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoaderLegacy.tsx
new file mode 100644
index 000000000000..a1a2fd69af7b
--- /dev/null
+++ b/packages/suite/src/components/suite/modals/ModalSwitcher/DiscoveryLoaderLegacy.tsx
@@ -0,0 +1,26 @@
+import styled from 'styled-components';
+import { Spinner } from '@trezor/components';
+import { Translation, Modal } from 'src/components/suite';
+
+const Expand = styled.div`
+ display: flex;
+ width: 100%;
+ justify-content: center;
+ margin: 40px 0;
+`;
+
+const StyledModal = styled(Modal)`
+ width: 360px;
+`;
+
+export const DiscoveryLoaderLegacy = () => (
+ }
+ description={}
+ data-test="@discovery/loader"
+ >
+
+
+
+
+);
diff --git a/packages/suite/src/components/suite/modals/ModalSwitcher/ModalSwitcher.tsx b/packages/suite/src/components/suite/modals/ModalSwitcher/ModalSwitcher.tsx
index acd442787cd2..e9fc18b95de3 100644
--- a/packages/suite/src/components/suite/modals/ModalSwitcher/ModalSwitcher.tsx
+++ b/packages/suite/src/components/suite/modals/ModalSwitcher/ModalSwitcher.tsx
@@ -2,17 +2,24 @@ import { usePreferredModal } from 'src/hooks/suite/usePreferredModal';
import { ReduxModal } from '../ReduxModal/ReduxModal';
import { ForegroundAppModal } from './ForegroundAppModal';
import { DiscoveryLoader } from './DiscoveryLoader';
+import { useSelector } from 'src/hooks/suite';
+import { DiscoveryLoaderLegacy } from './DiscoveryLoaderLegacy';
/** Displays whichever redux modal or foreground app should be displayed */
export const ModalSwitcher = () => {
+ const isViewOnlyModeVisible = useSelector(
+ state => state.suite.settings.debug.isViewOnlyModeVisible,
+ );
const modal = usePreferredModal();
+
+ // return ; // @TODO remove
switch (modal.type) {
case 'foreground-app':
return ;
case 'redux-modal':
return ;
case 'discovery-loading':
- return ;
+ return isViewOnlyModeVisible ? : ;
default:
return null;
}
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/DeviceContextModal.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/DeviceContextModal.tsx
index f6727735d970..8aa57f1ddc01 100644
--- a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/DeviceContextModal.tsx
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/DeviceContextModal.tsx
@@ -9,6 +9,7 @@ import {
PinModal,
PinInvalidModal,
PassphraseModal,
+ PassphraseModalLegacy,
PassphraseSourceModal,
PassphraseOnDeviceModal,
ConfirmActionModal,
@@ -29,6 +30,9 @@ export const DeviceContextModal = ({
}: ReduxModalProps) => {
const device = useSelector(selectDevice);
const intl = useIntl();
+ const isViewOnlyModeVisible = useSelector(
+ state => state.suite.settings.debug.isViewOnlyModeVisible,
+ );
if (!device) return null;
@@ -44,7 +48,11 @@ export const DeviceContextModal = ({
// Passphrase on host
case UI.REQUEST_PASSPHRASE:
- return ;
+ return isViewOnlyModeVisible ? (
+
+ ) : (
+
+ );
case 'WordRequestType_Plain':
return ;
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseDescription.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseDescription.tsx
new file mode 100644
index 000000000000..d9a819899db0
--- /dev/null
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseDescription.tsx
@@ -0,0 +1,21 @@
+import { Icon, Text } from '@trezor/components';
+import { PassphraseList, PassphraseItem } from './PassphraseList';
+
+export const PassphraseDescription = () => {
+ return (
+
+
+
+ Important to first learn how passphrase works
+
+
+
+ Passphrase opens a wallet secured by that phrase
+
+
+
+ No one can recover it, not even Trezor support
+
+
+ );
+};
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseHeading.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseHeading.tsx
new file mode 100644
index 000000000000..19414f1d8882
--- /dev/null
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseHeading.tsx
@@ -0,0 +1,8 @@
+import { H3 } from '@trezor/components';
+
+type PassphraseHeadingProps = {
+ children: React.ReactNode;
+};
+export const PassphraseHeading = ({ children }: PassphraseHeadingProps) => (
+ {children}
+);
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseList.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseList.tsx
new file mode 100644
index 000000000000..88a5106683ff
--- /dev/null
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseList.tsx
@@ -0,0 +1,16 @@
+import { spacingsPx, typography } from '@trezor/theme';
+import styled from 'styled-components';
+
+export const PassphraseList = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: ${spacingsPx.sm};
+ margin-top: ${spacingsPx.xs};
+ margin-bottom: ${spacingsPx.md};
+ justify-content: center;
+`;
+export const PassphraseItem = styled.div`
+ display: flex;
+ ${typography.hint};
+ gap: ${spacingsPx.xs};
+`;
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModal.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModal.tsx
index c989666ac7de..c5b17037ae90 100644
--- a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModal.tsx
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModal.tsx
@@ -1,52 +1,25 @@
import { useCallback, useState } from 'react';
import { useIntl } from 'react-intl';
-import styled from 'styled-components';
-
-import { variables, PassphraseTypeCard } from '@trezor/components';
+import { PassphraseTypeCard } from '@trezor/components';
import TrezorConnect from '@trezor/connect';
import * as deviceUtils from '@suite-common/suite-utils';
import {
selectIsDiscoveryAuthConfirmationRequired,
selectDevices,
onPassphraseSubmit,
+ selectDeviceModel,
} from '@suite-common/wallet-core';
-
import { useSelector, useDispatch } from 'src/hooks/suite';
-import { Translation, Modal } from 'src/components/suite';
+import { Translation } from 'src/components/suite';
import type { TrezorDevice } from 'src/types/suite';
import { OpenGuideFromTooltip } from 'src/components/guide';
import messages from 'src/support/messages';
-
-const Wrapper = styled.div<{ $authConfirmation?: boolean }>`
- display: flex;
- flex-direction: column;
- align-items: center;
-
- @media screen and (max-width: ${variables.SCREEN_SIZE.MD}) {
- width: 100%;
- }
-`;
-
-const WalletsWrapper = styled.div`
- display: flex;
- flex-direction: column;
- width: 100%;
-`;
-
-const Divider = styled.div`
- margin: 16px;
- height: 1px;
- background: ${({ theme }) => theme.STROKE_GREY};
-`;
-
-const TinyModal = styled(Modal)`
- width: 450px;
-`;
-
-const SmallModal = styled(Modal)`
- width: 600px;
-`;
+import { SwitchDeviceRenderer } from 'src/views/suite/SwitchDevice/SwitchDeviceRenderer';
+import { CardWithDevice } from 'src/views/suite/SwitchDevice/CardWithDevice';
+import { PassphraseDescription } from './PassphraseDescription';
+import { PassphraseWalletConfirmation } from './PassphraseWalletConfirmation';
+import { PassphraseHeading } from './PassphraseHeading';
interface PassphraseModalProps {
device: TrezorDevice;
@@ -57,12 +30,11 @@ export const PassphraseModal = ({ device }: PassphraseModalProps) => {
const devices = useSelector(selectDevices);
const authConfirmation =
useSelector(selectIsDiscoveryAuthConfirmationRequired) || device.authConfirm;
-
+ const deviceModel = useSelector(selectDeviceModel);
const stateConfirmation = !!device.state;
const hasEmptyPassphraseWallet = deviceUtils
.getDeviceInstances(device, devices)
.find(d => d.useEmptyPassphrase);
- const noPassphraseOffer = !hasEmptyPassphraseWallet && !stateConfirmation;
const onDeviceOffer = !!(
device.features &&
device.features.capabilities &&
@@ -83,108 +55,45 @@ export const PassphraseModal = ({ device }: PassphraseModalProps) => {
[setSubmitted, dispatch],
);
- const onRecreate = useCallback(() => {
- // Cancel TrezorConnect request and pass error to suiteAction.authConfirm
- TrezorConnect.cancel('auth-confirm-cancel');
- }, []);
-
if (submitted) {
return null;
}
- if (authConfirmation || stateConfirmation) {
- // show borderless one-column modal for confirming passphrase and state confirmation
+ const isPassphraseWalletConfirmationVisible = authConfirmation || stateConfirmation;
+ const isPassphraseWalletCreating = !hasEmptyPassphraseWallet && !stateConfirmation;
+
+ // show borderless one-column modal for confirming passphrase and state confirmation
+ if (isPassphraseWalletConfirmationVisible) {
return (
-
- ) : (
-
- )
- }
- isCancelable
+
- ) : (
-
- )
- }
- >
- }
- offerPassphraseOnDevice={onDeviceOffer}
- onSubmit={onSubmit}
- singleColModal
- learnMoreTooltipOnClick={
-
- }
- />
-
+ authConfirmation={authConfirmation}
+ onSubmit={onSubmit}
+ onDeviceOffer={onDeviceOffer}
+ device={device}
+ />
);
}
// creating a hidden wallet
- if (!noPassphraseOffer) {
+ if (isPassphraseWalletCreating) {
return (
- }
- description={}
- isCancelable
- onCancel={onCancel}
- >
- }
- description={}
- submitLabel={}
- type="hidden"
- singleColModal
- offerPassphraseOnDevice={onDeviceOffer}
- onSubmit={onSubmit}
- learnMoreTooltipOnClick={
-
- }
- />
-
- );
- }
+
+
+
+
+
- // show 2-column modal for selecting between standard and hidden wallets
- return (
- }
- >
-
-
- }
- description={}
- submitLabel={}
- type="standard"
- onSubmit={onSubmit}
- />
-
+
}
description={}
- submitLabel={}
+ submitLabel={}
type="hidden"
+ singleColModal
offerPassphraseOnDevice={onDeviceOffer}
onSubmit={onSubmit}
+ deviceModel={deviceModel ?? undefined}
learnMoreTooltipOnClick={
{
/>
}
/>
-
-
-
- );
+
+
+ );
+ }
+
+ // creating standard wallet here instead of showing dialog
+ onSubmit('');
+
+ return null;
};
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModalLegacy.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModalLegacy.tsx
new file mode 100644
index 000000000000..6e6156706438
--- /dev/null
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseModalLegacy.tsx
@@ -0,0 +1,199 @@
+import { useCallback, useState } from 'react';
+import { useIntl } from 'react-intl';
+
+import styled from 'styled-components';
+
+import { variables, PassphraseTypeCard } from '@trezor/components';
+import TrezorConnect from '@trezor/connect';
+import * as deviceUtils from '@suite-common/suite-utils';
+import {
+ selectIsDiscoveryAuthConfirmationRequired,
+ selectDevices,
+ onPassphraseSubmit,
+} from '@suite-common/wallet-core';
+
+import { useSelector, useDispatch } from 'src/hooks/suite';
+import { Translation, Modal } from 'src/components/suite';
+import type { TrezorDevice } from 'src/types/suite';
+import { OpenGuideFromTooltip } from 'src/components/guide';
+import messages from 'src/support/messages';
+
+const Wrapper = styled.div<{ $authConfirmation?: boolean }>`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ @media screen and (max-width: ${variables.SCREEN_SIZE.MD}) {
+ width: 100%;
+ }
+`;
+
+const WalletsWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+
+const Divider = styled.div`
+ margin: 16px;
+ height: 1px;
+ background: ${({ theme }) => theme.STROKE_GREY};
+`;
+
+const TinyModal = styled(Modal)`
+ width: 450px;
+`;
+
+const SmallModal = styled(Modal)`
+ width: 600px;
+`;
+
+interface PassphraseModalProps {
+ device: TrezorDevice;
+}
+
+export const PassphraseModalLegacy = ({ device }: PassphraseModalProps) => {
+ const [submitted, setSubmitted] = useState(false);
+ const devices = useSelector(selectDevices);
+ const authConfirmation =
+ useSelector(selectIsDiscoveryAuthConfirmationRequired) || device.authConfirm;
+
+ const stateConfirmation = !!device.state;
+ const hasEmptyPassphraseWallet = deviceUtils
+ .getDeviceInstances(device, devices)
+ .find(d => d.useEmptyPassphrase);
+ const noPassphraseOffer = !hasEmptyPassphraseWallet && !stateConfirmation;
+ const onDeviceOffer = !!(
+ device.features &&
+ device.features.capabilities &&
+ device.features.capabilities.includes('Capability_PassphraseEntry')
+ );
+
+ const dispatch = useDispatch();
+
+ const intl = useIntl();
+
+ const onCancel = () => TrezorConnect.cancel(intl.formatMessage(messages.TR_CANCELLED));
+
+ const onSubmit = useCallback(
+ (value: string, passphraseOnDevice?: boolean) => {
+ setSubmitted(true);
+ dispatch(onPassphraseSubmit({ value, passphraseOnDevice: !!passphraseOnDevice }));
+ },
+ [setSubmitted, dispatch],
+ );
+
+ const onRecreate = useCallback(() => {
+ // Cancel TrezorConnect request and pass error to suiteAction.authConfirm
+ TrezorConnect.cancel('auth-confirm-cancel');
+ }, []);
+
+ if (submitted) {
+ return null;
+ }
+
+ if (authConfirmation || stateConfirmation) {
+ // show borderless one-column modal for confirming passphrase and state confirmation
+ return (
+
+ ) : (
+
+ )
+ }
+ isCancelable
+ onCancel={onCancel}
+ onBackClick={authConfirmation ? onRecreate : undefined}
+ description={
+ !authConfirmation ? (
+
+ ) : (
+
+ )
+ }
+ >
+ }
+ offerPassphraseOnDevice={onDeviceOffer}
+ onSubmit={onSubmit}
+ singleColModal
+ learnMoreTooltipOnClick={
+
+ }
+ />
+
+ );
+ }
+
+ // creating a hidden wallet
+ if (!noPassphraseOffer) {
+ return (
+ }
+ description={}
+ isCancelable
+ onCancel={onCancel}
+ >
+ }
+ description={}
+ submitLabel={}
+ type="hidden"
+ singleColModal
+ offerPassphraseOnDevice={onDeviceOffer}
+ onSubmit={onSubmit}
+ learnMoreTooltipOnClick={
+
+ }
+ />
+
+ );
+ }
+
+ // show 2-column modal for selecting between standard and hidden wallets
+ return (
+ }
+ >
+
+
+ }
+ description={}
+ submitLabel={}
+ type="standard"
+ onSubmit={onSubmit}
+ />
+
+ }
+ description={}
+ submitLabel={}
+ type="hidden"
+ offerPassphraseOnDevice={onDeviceOffer}
+ onSubmit={onSubmit}
+ learnMoreTooltipOnClick={
+
+ }
+ />
+
+
+
+ );
+};
diff --git a/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseWalletConfirmation.tsx b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseWalletConfirmation.tsx
new file mode 100644
index 000000000000..23206bf6a5ae
--- /dev/null
+++ b/packages/suite/src/components/suite/modals/ReduxModal/DeviceContextModal/PassphraseWalletConfirmation.tsx
@@ -0,0 +1,192 @@
+import { TrezorDevice } from '@suite-common/suite-types';
+import {
+ Button,
+ Card,
+ Text,
+ PassphraseTypeCard,
+ Rows,
+ Columns,
+ Warning,
+ Icon,
+} from '@trezor/components';
+import { useState } from 'react';
+import { OpenGuideFromTooltip } from 'src/components/guide';
+import { Translation } from 'src/components/suite/Translation';
+import { CardWithDevice } from 'src/views/suite/SwitchDevice/CardWithDevice';
+import { SwitchDeviceRenderer } from 'src/views/suite/SwitchDevice/SwitchDeviceRenderer';
+import { PassphraseList, PassphraseItem } from './PassphraseList';
+import { PassphraseHeading } from './PassphraseHeading';
+// import styled from 'styled-components';
+
+interface PassphraseWalletConfirmationProps {
+ onCancel: () => void;
+ authConfirmation?: boolean;
+ onSubmit: (value: string, passphraseOnDevice?: boolean) => void;
+ onDeviceOffer: boolean;
+ device: TrezorDevice;
+}
+
+// const Columns = styled.div`
+// display: flex;
+// flex-direction: row;
+// justify-content: space-between;
+// gap: ${spacingsPx.xs};
+// `;
+// const Rows = styled.div`
+// display: flex;
+// flex-direction: column;
+// gap: ${spacingsPx.sm};
+// `;
+
+type ContentType = 'step1' | 'step2' | 'step3';
+
+export const PassphraseWalletConfirmation = ({
+ onCancel,
+ authConfirmation,
+ onSubmit,
+ onDeviceOffer,
+ device,
+}: PassphraseWalletConfirmationProps) => {
+ const [contentType, setContentType] = useState('step1');
+
+ const getContent = () => {
+ if (contentType === 'step1') {
+ return (
+ <>
+ This passphrase wallet is empty
+
+
+ Learn how a passphrase works
+
+
+ }
+ >
+
+
+ Opening unused and knowingly empty passphrase wallet?
+
+
+
+
+
+
+
+
+ Expecting passphrase wallet with funds?
+
+
+
+
+
+ >
+ );
+ }
+
+ if (contentType === 'step2')
+ return (
+ <>
+ What to do with new passphrase?
+
+
+
+
+ Write your passphrase on a piece of paper and always keep it offline
+ (no photos, USB, internet)
+
+
+
+
+ Store it in a different place than your backup seed
+
+
+
+ Never share it with anyone, not even with Trezor support
+
+
+
+ No one can recover it, not even Trezor support
+
+ >
+ );
+ if (contentType === 'step3')
+ return (
+ <>
+ Confirm passphrase
+
+ Create an offline backup of your passphrase. It is irrecoverable, even by
+ Trezor support.
+
+ }
+ offerPassphraseOnDevice={onDeviceOffer}
+ onSubmit={onSubmit}
+ singleColModal
+ learnMoreTooltipOnClick={
+
+ }
+ />
+ >
+ );
+ };
+
+ return (
+
+ {/*
+ ) : (
+
+ )
+ }
+ isCancelable
+ onCancel={onCancel}
+ onBackClick={authConfirmation ? onRecreate : undefined}
+ description={
+ !authConfirmation ? (
+
+ ) : (
+
+ )
+ }
+ > */}
+
+ {getContent()}
+
+
+ );
+};
diff --git a/packages/suite/src/components/suite/modals/index.tsx b/packages/suite/src/components/suite/modals/index.tsx
index 1faae571c2b4..fb653597b1e6 100644
--- a/packages/suite/src/components/suite/modals/index.tsx
+++ b/packages/suite/src/components/suite/modals/index.tsx
@@ -2,6 +2,7 @@ export { PinModal } from './ReduxModal/DeviceContextModal/PinModal';
export { PinInvalidModal } from './ReduxModal/DeviceContextModal/PinInvalidModal';
export { PinMismatchModal } from './ReduxModal/UserContextModal/PinMismatchModal';
export { PassphraseModal } from './ReduxModal/DeviceContextModal/PassphraseModal';
+export { PassphraseModalLegacy } from './ReduxModal/DeviceContextModal/PassphraseModalLegacy';
export { PassphraseSourceModal } from './ReduxModal/DeviceContextModal/PassphraseSourceModal';
export { PassphraseOnDeviceModal } from './ReduxModal/DeviceContextModal/PassphraseOnDeviceModal';
export { PassphraseDuplicateModal } from './ReduxModal/UserContextModal/PassphraseDuplicateModal';
diff --git a/packages/suite/src/views/suite/SwitchDevice/CardWithDevice.tsx b/packages/suite/src/views/suite/SwitchDevice/CardWithDevice.tsx
new file mode 100644
index 000000000000..c1e229094766
--- /dev/null
+++ b/packages/suite/src/views/suite/SwitchDevice/CardWithDevice.tsx
@@ -0,0 +1,83 @@
+import { ReactNode, useState } from 'react';
+
+import { AnimatePresence, motion } from 'framer-motion';
+import styled from 'styled-components';
+import { Card, motionAnimation, useElevation } from '@trezor/components';
+import * as deviceUtils from '@suite-common/suite-utils';
+
+import type { TrezorDevice, ForegroundAppProps } from 'src/types/suite';
+import { Elevation, mapElevationToBorder, spacingsPx } from '@trezor/theme';
+
+import { DeviceHeader } from './DeviceItem/DeviceHeader';
+
+const Content = styled.div<{ $elevation: Elevation }>`
+ padding-top: ${spacingsPx.xs};
+ position: relative;
+
+ &::before {
+ height: 1px;
+ content: '';
+ background-color: ${({ $elevation, theme }) => mapElevationToBorder({ $elevation, theme })};
+ position: absolute;
+ left: -12px;
+ right: -12px;
+ top: 0;
+ }
+`;
+
+const DeviceWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ gap: ${spacingsPx.xs};
+
+ & + & {
+ margin-top: ${spacingsPx.xxxl};
+ }
+`;
+
+interface CardWithDeviceProps {
+ children: ReactNode;
+ deviceWarning?: ReactNode;
+ onCancel?: ForegroundAppProps['onCancel'];
+ device: TrezorDevice;
+}
+
+export const CardWithDevice = ({
+ children,
+ onCancel,
+ device,
+ deviceWarning,
+}: CardWithDeviceProps) => {
+ const deviceStatus = deviceUtils.getStatus(device);
+ const [isExpanded, setIsExpanded] = useState(true);
+
+ const needsAttention = deviceUtils.deviceNeedsAttention(deviceStatus);
+ const isUnknown = device.type !== 'acquired';
+ const { elevation } = useElevation();
+
+ return (
+
+
+
+
+ {deviceWarning}
+
+ {!needsAttention && (
+
+ {!isUnknown && isExpanded && (
+
+ {children}
+
+ )}
+
+ )}
+
+
+ );
+};
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/AddWalletButton.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/AddWalletButton.tsx
index 39852ff268af..ec841fc20e79 100644
--- a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/AddWalletButton.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/AddWalletButton.tsx
@@ -1,11 +1,12 @@
import styled from 'styled-components';
-import { Button, Tooltip } from '@trezor/components';
+import { Button, HotkeyBadge, Tooltip } from '@trezor/components';
import { Translation } from 'src/components/suite';
import { TrezorDevice, AcquiredDevice } from 'src/types/suite';
import { useSelector } from 'src/hooks/suite';
import { SUITE } from 'src/actions/suite/constants';
+import { spacingsPx } from '@trezor/theme';
const AddWallet = styled.div`
display: flex;
@@ -17,6 +18,19 @@ const StyledTooltip = styled(Tooltip)`
width: 100%;
`;
+const Rows = styled.div`
+ display: flex;
+ flex-direction: row;
+ gap: ${spacingsPx.xs};
+`;
+
+const Columns = styled.div`
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ gap: ${spacingsPx.xs};
+`;
+
interface AddWalletButtonProps {
device: TrezorDevice;
instances: AcquiredDevice[];
@@ -28,6 +42,8 @@ export const AddWalletButton = ({
device,
instances,
addDeviceInstance,
+ // addStandardWallet,
+ // addPassphraseWallet,
selectDeviceInstance,
}: AddWalletButtonProps) => {
const hasAtLeastOneWallet = instances.find(d => d.state);
@@ -44,7 +60,8 @@ export const AddWalletButton = ({
locks.includes(SUITE.LOCK_TYPE.DEVICE) ||
locks.includes(SUITE.LOCK_TYPE.UI);
- const onAddWallet = () => {
+ // @TODO fix buttons
+ const onAddWallet = (isPassphraseWallet: boolean) => {
if (hasAtLeastOneWallet) {
addDeviceInstance(device);
} else {
@@ -59,24 +76,34 @@ export const AddWalletButton = ({
cursor="pointer"
placement="bottom"
>
-
+
+ onAddWallet(true)}
+ >
+
+
+ {!isLocked && }
+
+
+
);
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceHeader.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceHeader.tsx
new file mode 100644
index 000000000000..e118ca8aa442
--- /dev/null
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceHeader.tsx
@@ -0,0 +1,77 @@
+import styled, { useTheme } from 'styled-components';
+import { Icon } from '@trezor/components';
+import { DeviceStatus } from 'src/components/suite/layouts/SuiteLayout/DeviceSelector/DeviceStatus';
+import { isWebUsb } from 'src/utils/suite/transport';
+import { WebUsbButton } from 'src/components/suite';
+import { spacingsPx } from '@trezor/theme';
+import { useSelector } from 'src/hooks/suite';
+import { motion } from 'framer-motion';
+import { ForegroundAppProps, TrezorDevice } from 'src/types/suite';
+
+const Flex = styled.div`
+ flex: 1;
+`;
+const Container = styled.div`
+ display: flex;
+ align-items: center;
+ flex: 1;
+ cursor: pointer;
+`;
+
+const DeviceActions = styled.div`
+ display: flex;
+ align-items: center;
+ margin-left: ${spacingsPx.lg};
+ gap: ${spacingsPx.xxs};
+`;
+
+interface DeviceHeaderProps {
+ device: TrezorDevice;
+ onCancel?: ForegroundAppProps['onCancel'];
+ setIsExpanded: (expanded: boolean) => void;
+ isExpanded: boolean;
+}
+
+export const DeviceHeader = ({
+ onCancel,
+ device,
+ setIsExpanded,
+ isExpanded,
+}: DeviceHeaderProps) => {
+ const transport = useSelector(state => state.suite.transport);
+ const isWebUsbTransport = isWebUsb(transport);
+ const theme = useTheme();
+ const deviceModelInternal = device.features?.internal_model;
+
+ return (
+ onCancel?.()}>
+
+ {deviceModelInternal && (
+
+ )}
+
+
+
+ {isWebUsbTransport && }
+
+ {
+ setIsExpanded(!isExpanded);
+ e.stopPropagation();
+ }}
+ />
+
+
+
+ );
+};
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceItem.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceItem.tsx
index d4bb33a53223..be8246b66142 100644
--- a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceItem.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceItem.tsx
@@ -1,52 +1,20 @@
-import { useState } from 'react';
-
-import { AnimatePresence, motion } from 'framer-motion';
-import styled, { useTheme } from 'styled-components';
-import { variables, Icon, motionAnimation } from '@trezor/components';
+import styled from 'styled-components';
+import { variables } from '@trezor/components';
import * as deviceUtils from '@suite-common/suite-utils';
-import {
- selectDevice,
- acquireDevice,
- createDeviceInstance,
- selectDeviceThunk,
-} from '@suite-common/wallet-core';
+import { selectDevice, createDeviceInstance, selectDeviceThunk } from '@suite-common/wallet-core';
import { useDispatch, useSelector } from 'src/hooks/suite';
import { goto } from 'src/actions/suite/routerActions';
import { WalletInstance } from './WalletInstance';
import { AddWalletButton } from './AddWalletButton';
-import { DeviceHeaderButton } from './DeviceHeaderButton';
+import { acquireDevice } from '@suite-common/wallet-core';
import type { TrezorDevice, AcquiredDevice, ForegroundAppProps } from 'src/types/suite';
import type { getBackgroundRoute } from 'src/utils/suite/router';
import { spacingsPx } from '@trezor/theme';
-import { DeviceStatus } from 'src/components/suite/layouts/SuiteLayout/DeviceSelector/DeviceStatus';
-import { isWebUsb } from 'src/utils/suite/transport';
-import { WebUsbButton } from 'src/components/suite';
-
-const DeviceWrapper = styled.div`
- display: flex;
- flex-direction: column;
- width: 100%;
- gap: ${spacingsPx.xs};
-
- & + & {
- margin-top: ${spacingsPx.xxxl};
- }
-`;
-
-const Device = styled.div`
- display: flex;
- align-items: center;
-`;
-
-const DeviceActions = styled.div`
- display: flex;
- align-items: center;
- margin-left: ${spacingsPx.lg};
- gap: ${spacingsPx.xxs};
-`;
+import { CardWithDevice } from '../CardWithDevice';
+import { DeviceWarning } from './DeviceWarning';
const WalletsWrapper = styled.div<{ $enabled: boolean }>`
opacity: ${({ $enabled }) => ($enabled ? 1 : 0.5)};
@@ -64,13 +32,6 @@ const InstancesWrapper = styled.div`
gap: ${spacingsPx.xs};
`;
-const DeviceHeader = styled.div`
- display: flex;
- align-items: center;
- flex: 1;
- cursor: pointer;
-`;
-
interface DeviceItemProps {
device: TrezorDevice;
instances: AcquiredDevice[];
@@ -81,17 +42,8 @@ interface DeviceItemProps {
export const DeviceItem = ({ device, instances, onCancel, backgroundRoute }: DeviceItemProps) => {
const selectedDevice = useSelector(selectDevice);
const dispatch = useDispatch();
- const transport = useSelector(state => state.suite.transport);
-
- const isWebUsbTransport = isWebUsb(transport);
- const theme = useTheme();
- const [isExpanded, setIsExpanded] = useState(true);
-
const deviceStatus = deviceUtils.getStatus(device);
- const deviceModelInternal = device.features?.internal_model;
-
const needsAttention = deviceUtils.deviceNeedsAttention(deviceStatus);
- const isUnknown = device.type !== 'acquired';
const instancesWithState = instances.filter(i => i.state);
const handleRedirection = async () => {
@@ -122,6 +74,14 @@ export const DeviceItem = ({ device, instances, onCancel, backgroundRoute }: Dev
await dispatch(createDeviceInstance({ device: instance }));
handleRedirection();
};
+ // const addStandardWallet = async (instance: DeviceItemProps['device']) => {
+ // await dispatch(createDeviceInstance({ device: instance, useEmptyPassphrase: true }));
+ // handleRedirection();
+ // };
+ // const addPassphraseWallet = async (instance: DeviceItemProps['device']) => {
+ // await dispatch(createDeviceInstance({ device: instance, useEmptyPassphrase: false }));
+ // handleRedirection();
+ // };
const onSolveIssueClick = () => {
const needsAcquire =
@@ -136,70 +96,40 @@ export const DeviceItem = ({ device, instances, onCancel, backgroundRoute }: Dev
};
return (
-
-
- onCancel()}>
- {deviceModelInternal && (
-
- )}
-
-
- {isWebUsbTransport && }
-
- setIsExpanded(!isExpanded)}
- />
-
-
-
-
-
- {!needsAttention && (
-
- {!isUnknown && isExpanded && (
-
-
-
- {instancesWithState.map((instance, index) => (
-
- ))}
-
-
-
-
-
- )}
-
- )}
-
+
+ }
+ onCancel={onCancel}
+ device={device}
+ >
+
+
+ {instancesWithState.map((instance, index) => (
+
+ ))}
+
+
+
+
+
);
};
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceHeaderButton.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceWarning.tsx
similarity index 90%
rename from packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceHeaderButton.tsx
rename to packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceWarning.tsx
index 3edce5e2846d..d70d6933ab3d 100644
--- a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceHeaderButton.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/DeviceWarning.tsx
@@ -3,17 +3,17 @@ import * as deviceUtils from '@suite-common/suite-utils';
import { NotificationCard, Translation } from 'src/components/suite';
import { TrezorDevice } from 'src/types/suite';
-interface DeviceHeaderButtonProps {
+interface DeviceWarningProps {
needsAttention: boolean;
device: TrezorDevice;
onSolveIssueClick: () => void;
}
-export const DeviceHeaderButton = ({
+export const DeviceWarning = ({
device,
needsAttention,
onSolveIssueClick,
-}: DeviceHeaderButtonProps) => {
+}: DeviceWarningProps) => {
const deviceStatus = deviceUtils.getStatus(device);
const deviceStatusMessage = deviceUtils.getDeviceNeedsAttentionMessage(deviceStatus);
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/EjectButton.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/EjectButton.tsx
new file mode 100644
index 000000000000..b5083ee708cc
--- /dev/null
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/EjectButton.tsx
@@ -0,0 +1,42 @@
+import styled, { useTheme } from 'styled-components';
+
+import { Tooltip, Icon } from '@trezor/components';
+import { Translation } from 'src/components/suite';
+import { borders, spacingsPx } from '@trezor/theme';
+import { ContentType } from '../types';
+
+const EjectContainer = styled.div`
+ position: absolute;
+ right: ${spacingsPx.xs};
+ top: ${spacingsPx.xs};
+ background-color: white;
+ border-radius: ${borders.radii.full};
+ padding: ${spacingsPx.xxs};
+`;
+
+interface EjectButtonProps {
+ setContentType: (contentType: ContentType) => void;
+ dataTest?: string;
+}
+
+export const EjectButton = ({ setContentType, dataTest }: EjectButtonProps) => {
+ const theme = useTheme();
+
+ return (
+
+ }>
+ {
+ setContentType('ejectConfirmation');
+ e.stopPropagation();
+ }}
+ />
+
+
+ );
+};
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnly.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnly.tsx
index f7e26bd2dd00..69ed582b1eef 100644
--- a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnly.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnly.tsx
@@ -1,6 +1,6 @@
-import styled, { useTheme } from 'styled-components';
+import styled from 'styled-components';
-import { CollapsibleBox, Text, Tooltip, Icon } from '@trezor/components';
+import { CollapsibleBox, Text } from '@trezor/components';
import { Translation } from 'src/components/suite';
import { ViewOnlyRadios } from './ViewOnlyRadios';
import { spacingsPx } from '@trezor/theme';
@@ -25,12 +25,6 @@ const ViewOnlyContent = styled.div`
align-items: center;
`;
-const EjectContainer = styled.div`
- position: absolute;
- right: ${spacingsPx.sm};
- top: ${spacingsPx.sm};
-`;
-
const Circle = styled.div<{ $isHighlighted?: boolean }>`
width: 6px;
height: 6px;
@@ -42,72 +36,52 @@ const Circle = styled.div<{ $isHighlighted?: boolean }>`
export const ViewOnly = ({ setContentType, instance, dataTest }: ViewOnlyProps) => {
const [isViewOnlyExpanded, setIsViewOnlyExpanded] = useState(false);
const dispatch = useDispatch();
- const theme = useTheme();
+
const isViewOnly = !!instance.remember;
- const handleRememberChange = (value: boolean) => {
+ const handleRememberChange = () => {
setContentType('default');
- setIsViewOnlyExpanded(false);
dispatch(
toggleRememberDevice({
device: instance,
- forceRemember: value === true ? true : undefined,
}),
);
};
return (
- <>
- {
- e.stopPropagation();
- }}
+ {
+ e.stopPropagation();
+ }}
+ >
+ setIsViewOnlyExpanded(!isViewOnlyExpanded)}
+ heading={
+
+
+
+ {isViewOnly ? (
+
+ ) : (
+
+ )}
+
+
+ }
>
- setIsViewOnlyExpanded(!isViewOnlyExpanded)}
- heading={
-
-
-
- {isViewOnly ? (
-
- ) : (
-
- )}
-
-
- }
- >
-
-
-
-
-
- }>
- {
- setContentType('ejectConfirmation');
- e.stopPropagation();
- }}
- />
-
-
- >
+
+
+
);
};
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnlyRadios.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnlyRadios.tsx
index 32cf00be81ca..5fcfd7a4f3bd 100644
--- a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnlyRadios.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/ViewOnlyRadios.tsx
@@ -3,11 +3,10 @@ import styled from 'styled-components';
import { Text, Radio, Button, Icon, useElevation } from '@trezor/components';
import { Elevation, borders, mapElevationToBorder, spacingsPx, typography } from '@trezor/theme';
import { Translation } from 'src/components/suite';
-import { useState } from 'react';
type ViewOnlyRadiosProps = {
isViewOnlyActive: boolean;
- setIsViewOnlyActive: (isViewOnlyActive: boolean) => void;
+ toggleViewOnly: () => void;
dataTest?: string;
};
type ViewOnlyRadioProps = {
@@ -75,20 +74,22 @@ export const ViewOnlyRadio = ({
};
export const ViewOnlyRadios = ({
isViewOnlyActive,
- setIsViewOnlyActive,
+ toggleViewOnly,
dataTest,
}: ViewOnlyRadiosProps) => {
- const [isViewOnlyActiveTemp, setIsViewOnlyActiveTemp] = useState(isViewOnlyActive);
- const handleConfirm = () => {
- setIsViewOnlyActive(isViewOnlyActiveTemp);
+ const handleConfirm = (newValue: boolean) => {
+ const isValueChanged = isViewOnlyActive !== newValue;
+ if (isValueChanged) {
+ toggleViewOnly();
+ }
};
return (
}
- onClick={() => setIsViewOnlyActiveTemp(true)}
- isChecked={isViewOnlyActiveTemp}
+ onClick={() => handleConfirm(true)}
+ isChecked={isViewOnlyActive}
dataTest={`${dataTest}/enabled`}
>
}
- onClick={() => setIsViewOnlyActiveTemp(false)}
- isChecked={!isViewOnlyActiveTemp}
+ onClick={() => handleConfirm(false)}
+ isChecked={!isViewOnlyActive}
dataTest={`${dataTest}/disabled`}
>
-
-
-
-
-
diff --git a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/WalletInstance.tsx b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/WalletInstance.tsx
index 5749c80a2880..69df092e29c3 100644
--- a/packages/suite/src/views/suite/SwitchDevice/DeviceItem/WalletInstance.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/DeviceItem/WalletInstance.tsx
@@ -22,6 +22,7 @@ import { useState } from 'react';
import { EjectConfirmation } from './EjectConfirmation';
import { ContentType } from '../types';
import { ViewOnly } from './ViewOnly';
+import { EjectButton } from './EjectButton';
const InstanceType = styled.div<{ isSelected: boolean }>`
display: flex;
@@ -111,6 +112,7 @@ export const WalletInstance = ({
{...rest}
>
{isSelected && }
+
{discoveryProcess && (
@@ -163,7 +165,6 @@ export const WalletInstance = ({
instance={instance}
/>
)}
-
{contentType === 'ejectConfirmation' && (
{
@@ -38,37 +33,19 @@ export const SwitchDevice = ({ cancelable, onCancel }: ForegroundAppProps) => {
const backgroundRoute = getBackgroundRoute();
- const initial = {
- width: 279,
- height: 70,
- };
-
return (
-
-
- <>
- {sortedDevices.map(device => (
-
-
-
- ))}
- >
-
-
+
+ {sortedDevices.map(device => (
+
+ ))}
+
);
};
diff --git a/packages/suite/src/views/suite/SwitchDevice/SwitchDeviceModal.tsx b/packages/suite/src/views/suite/SwitchDevice/SwitchDeviceModal.tsx
index bb9b3c319857..40e73a7b15ca 100644
--- a/packages/suite/src/views/suite/SwitchDevice/SwitchDeviceModal.tsx
+++ b/packages/suite/src/views/suite/SwitchDevice/SwitchDeviceModal.tsx
@@ -1,7 +1,7 @@
-import { ElevationContext } from '@trezor/components';
import { useEvent } from 'react-use';
import styled from 'styled-components';
-
+import { motion } from 'framer-motion';
+import { spacingsPx } from '@trezor/theme';
type SwitchDeviceModalProps = {
children?: React.ReactNode;
isCancelable?: boolean;
@@ -15,6 +15,19 @@ const Container = styled.div`
margin: 5px;
`;
+const DeviceItemsWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: ${spacingsPx.md};
+ flex: 1;
+`;
+
+const initial = {
+ width: 279,
+ height: 70,
+};
+
export const SwitchDeviceModal = ({
children,
onCancel,
@@ -27,13 +40,23 @@ export const SwitchDeviceModal = ({
});
return (
-
- e.stopPropagation()} // needed because of the Backdrop implementation
- data-test={dataTest}
- >
- {children}
-
-
+ e.stopPropagation()} // needed because of the Backdrop implementation
+ data-test={dataTest}
+ >
+
+
+ {children}
+
+
+
);
};