Skip to content

Commit

Permalink
fix(suite-native): fix and refactor selectors for empty device states
Browse files Browse the repository at this point in the history
  • Loading branch information
matejkriz committed May 23, 2024
1 parent acd9e1e commit b6ccc94
Show file tree
Hide file tree
Showing 10 changed files with 42 additions and 53 deletions.
23 changes: 7 additions & 16 deletions suite-common/wallet-core/src/accounts/accountsReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { formattedAccountTypeMap } from './accountsConstants';
import {
DeviceRootState,
selectDevice,
selectIsNoPhysicalDeviceConnected,
selectIsPortfolioTrackerDevice,
selectHasOnlyPortfolioDevice,
} from '../device/deviceReducer';
import { DiscoveryRootState, selectIsDeviceDiscoveryActive } from '../discovery/discoveryReducer';

Expand Down Expand Up @@ -356,7 +355,8 @@ export const selectAccountsSymbols = memoize(
export const selectIsDeviceAccountless = (state: AccountsRootState & DeviceRootState) =>
pipe(selectDeviceAccounts(state), A.isEmpty);

export const selectIsDeviceDiscoveryEmpty = (
// Selected device has no accounts and no active discovery. It can be empty portfolio device.
export const selectIsEmptyDevice = (
state: AccountsRootState & DeviceRootState & DiscoveryRootState,
) => {
const isDeviceAccountless = selectIsDeviceAccountless(state);
Expand All @@ -365,20 +365,11 @@ export const selectIsDeviceDiscoveryEmpty = (
return isDeviceAccountless && !isDeviceDiscoveryActive;
};

export const selectAreAllDevicesDisconnectedOrAccountless = (
export const selectHasOnlyEmptyPortfolioTracker = (
state: AccountsRootState & DeviceRootState & DiscoveryRootState,
) => {
const isDeviceDiscoveryEmpty = selectIsDeviceDiscoveryEmpty(state);
const isNoPhysicalDeviceConnected = selectIsNoPhysicalDeviceConnected(state);
const isEmptyDevice = selectIsEmptyDevice(state);
const hasOnlyPortfolioDevice = selectHasOnlyPortfolioDevice(state);

return isDeviceDiscoveryEmpty && isNoPhysicalDeviceConnected;
};

export const selectIsPortfolioTrackerEmpty = (
state: AccountsRootState & DeviceRootState & DiscoveryRootState,
) => {
const isPortfolioTrackerDevice = selectIsPortfolioTrackerDevice(state);
const isDeviceDiscoveryEmpty = selectIsDeviceDiscoveryEmpty(state);

return isPortfolioTrackerDevice && isDeviceDiscoveryEmpty;
return isEmptyDevice && hasOnlyPortfolioDevice;
};
6 changes: 6 additions & 0 deletions suite-common/wallet-core/src/device/deviceReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,12 @@ export const selectIsNoPhysicalDeviceConnected = (state: DeviceRootState) => {
return devices.every(device => !device.connected);
};

export const selectHasOnlyPortfolioDevice = (state: DeviceRootState) => {
const devices = selectDevices(state);

return devices.length === 1 && devices[0].id === PORTFOLIO_TRACKER_DEVICE_ID;
};

export const selectIsDeviceBitcoinOnly = (state: DeviceRootState) => {
const features = selectDeviceFeatures(state);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
selectDeviceByState,
DeviceRootState,
PORTFOLIO_TRACKER_DEVICE_ID,
selectAreAllDevicesDisconnectedOrAccountless,
selectHasOnlyEmptyPortfolioTracker,
} from '@suite-common/wallet-core';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';
import { TypographyStyle } from '@trezor/theme';
Expand Down Expand Up @@ -57,9 +57,7 @@ export const DeviceItemContent = ({
const { applyStyle } = useNativeStyles();

const device = useSelector((state: DeviceRootState) => selectDeviceByState(state, deviceState));
const areAllDevicesDisconnectedOrAccountless = useSelector(
selectAreAllDevicesDisconnectedOrAccountless,
);
const hasOnlyEmptyPortfolioTracker = useSelector(selectHasOnlyEmptyPortfolioTracker);

const isPortfolioTrackerDevice = device?.id === PORTFOLIO_TRACKER_DEVICE_ID;

Expand All @@ -82,9 +80,7 @@ export const DeviceItemContent = ({

return (
<HStack style={applyStyle(contentWrapperStyle, { height: isCompact ? 46 : 56 })}>
<DeviceItemIcon
deviceId={areAllDevicesDisconnectedOrAccountless ? undefined : device.id}
/>
<DeviceItemIcon deviceId={hasOnlyEmptyPortfolioTracker ? undefined : device.id} />
<Box style={applyStyle(itemStyle, { isCompact })}>
{variant === 'simple' ? (
<SimpleDeviceItemContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Translation } from '@suite-native/intl';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';
import {
DeviceRootState,
selectAreAllDevicesDisconnectedOrAccountless,
selectHasOnlyEmptyPortfolioTracker,
selectDeviceByState,
} from '@suite-common/wallet-core';
import { TypographyStyle } from '@trezor/theme';
Expand Down Expand Up @@ -36,21 +36,16 @@ export const SimpleDeviceItemContent = ({
}: SimpleDeviceItemContentProps) => {
const { applyStyle } = useNativeStyles();
const device = useSelector((state: DeviceRootState) => selectDeviceByState(state, deviceState));
const areAllDevicesDisconnectedOrAccountless = useSelector(
selectAreAllDevicesDisconnectedOrAccountless,
);
const hasOnlyEmptyPortfolioTracker = useSelector(selectHasOnlyEmptyPortfolioTracker);

if (!device) {
return null;
}

const isPortfolioTrackerSubHeaderVisible =
isPortfolioTrackerDevice &&
!areAllDevicesDisconnectedOrAccountless &&
!isSubHeaderForceHidden;
isPortfolioTrackerDevice && !hasOnlyEmptyPortfolioTracker && !isSubHeaderForceHidden;

const isConnectionStateVisible =
!isPortfolioTrackerDevice && !areAllDevicesDisconnectedOrAccountless;
const isConnectionStateVisible = !isPortfolioTrackerDevice && !hasOnlyEmptyPortfolioTracker;

return (
<>
Expand All @@ -60,7 +55,7 @@ export const SimpleDeviceItemContent = ({
numberOfLines={1}
style={applyStyle(headerStyle)}
>
{areAllDevicesDisconnectedOrAccountless ? (
{hasOnlyEmptyPortfolioTracker ? (
<Translation id="deviceManager.defaultHeader" />
) : (
header
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
RootStackRoutes,
StackToStackCompositeNavigationProps,
} from '@suite-native/navigation';
import { selectIsDeviceDiscoveryEmpty } from '@suite-common/wallet-core';
import { selectIsEmptyDevice } from '@suite-common/wallet-core';
import { useOpenLink } from '@suite-native/link';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';

Expand All @@ -37,7 +37,7 @@ export const PortfolioTrackerDeviceManagerContent = () => {
const openLink = useOpenLink();
const { applyStyle } = useNativeStyles();

const isDeviceDiscoveryEmpty = useSelector(selectIsDeviceDiscoveryEmpty);
const isEmptyDevice = useSelector(selectIsEmptyDevice);

const navigation = useNavigation<NavigationProp>();

Expand Down Expand Up @@ -73,7 +73,7 @@ export const PortfolioTrackerDeviceManagerContent = () => {
const syncButtonTitle = (
<Translation
id={
isDeviceDiscoveryEmpty
isEmptyDevice
? 'deviceManager.syncCoinsButton.syncMyCoins'
: 'deviceManager.syncCoinsButton.syncAnother'
}
Expand Down
5 changes: 4 additions & 1 deletion suite-native/device/src/hooks/useDetectDeviceError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ export const useDetectDeviceError = () => {
]);

useEffect(() => {
// Hide the error alert on disconnect of the device
// Hide the error alert when the device is disconnected.
// Device with error can't be view-only.
// Edge case: If user has connected two devices simultaneously,
// it will not hide the alert.
if (isNoPhysicalDeviceConnected) {
hideAlert();
}
Expand Down
6 changes: 3 additions & 3 deletions suite-native/device/src/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
selectDeviceModel,
selectIsConnectedDeviceUninitialized,
selectIsDeviceConnectedAndAuthorized,
selectIsDeviceDiscoveryEmpty,
selectIsEmptyDevice,
selectIsUnacquiredDevice,
} from '@suite-common/wallet-core';

Expand Down Expand Up @@ -34,7 +34,7 @@ export const selectIsDeviceReadyToUseAndAuthorized = (
) => {
const isDeviceReadyToUse = selectIsDeviceReadyToUse(state);
const isDeviceConnectedAndAuthorized = selectIsDeviceConnectedAndAuthorized(state);
const isDeviceDiscoveryEmpty = selectIsDeviceDiscoveryEmpty(state);
const isEmptyDevice = selectIsEmptyDevice(state);

return isDeviceReadyToUse && isDeviceConnectedAndAuthorized && !isDeviceDiscoveryEmpty;
return isDeviceReadyToUse && isDeviceConnectedAndAuthorized && !isEmptyDevice;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useRef } from 'react';
import { useSelector } from 'react-redux';

import {
selectIsDeviceDiscoveryEmpty,
selectIsEmptyDevice,
selectIsDeviceAuthorized,
selectDeviceAuthFailed,
selectIsDeviceUnlocked,
Expand All @@ -19,19 +19,19 @@ import { EnableViewOnlyBottomSheet } from './components/EnableViewOnlyBottomShee
import { PortfolioGraphRef } from './components/PortfolioGraph';

export const HomeScreen = () => {
const isDeviceDiscoveryEmpty = useSelector(selectIsDeviceDiscoveryEmpty);
const isEmptyDevice = useSelector(selectIsEmptyDevice);
const isDeviceAuthorized = useSelector(selectIsDeviceAuthorized);
const isDeviceAuthFailed = useSelector(selectDeviceAuthFailed);
const isDeviceUnlocked = useSelector(selectIsDeviceUnlocked);
const isEmptyHomeRendererShown =
isDeviceDiscoveryEmpty && // There has to be no accounts and discovery not active.
isEmptyDevice && // There has to be no accounts and discovery not active.
(isDeviceAuthorized || // Initial state is empty portfolio device, that is authorized.
isDeviceAuthFailed || // When user click cancel on PIN entry or it fails from other reason.
!isDeviceUnlocked); // When user click cancel, it takes some time before isDeviceAuthFailed is set.

const portfolioContentRef = useRef<PortfolioGraphRef>(null);
const refreshControl = useHomeRefreshControl({
isPortfolioEmpty: isDeviceDiscoveryEmpty,
isEmptyDevice,
portfolioContentRef,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useSelector } from 'react-redux';

import { FeatureFlag, useFeatureFlag } from '@suite-native/feature-flags';
import {
selectAreAllDevicesDisconnectedOrAccountless,
selectHasOnlyEmptyPortfolioTracker,
selectIsDeviceAuthorized,
selectIsPortfolioTrackerDevice,
} from '@suite-common/wallet-core';
Expand All @@ -17,9 +17,7 @@ export const EmptyHomeRenderer = () => {

const isDeviceAuthorized = useSelector(selectIsDeviceAuthorized);
const isPortfolioTrackerDevice = useSelector(selectIsPortfolioTrackerDevice);
const areAllDevicesDisconnectedOrAccountless = useSelector(
selectAreAllDevicesDisconnectedOrAccountless,
);
const hasOnlyEmptyPortfolioTracker = useSelector(selectHasOnlyEmptyPortfolioTracker);
const isDeviceReadyToUse = useSelector(selectIsDeviceReadyToUse);

// This state is present only for a fraction of second while redirecting to the Connecting screen is already happening.
Expand All @@ -31,7 +29,7 @@ export const EmptyHomeRenderer = () => {
if (isUsbDeviceConnectFeatureEnabled) {
// Crossroads should be displayed if there is no real device connected and portfolio tracker has no accounts
// or if there is device connected, but not authorized (PIN enter cancelled).
if (areAllDevicesDisconnectedOrAccountless || !isDeviceAuthorized) {
if (hasOnlyEmptyPortfolioTracker || !isDeviceAuthorized) {
return <EmptyPortfolioCrossroads />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { useNativeStyles } from '@trezor/styles';
import { PortfolioGraphRef } from './components/PortfolioGraph';

export const useHomeRefreshControl = ({
isPortfolioEmpty,
isEmptyDevice,
portfolioContentRef,
}: {
isPortfolioEmpty: boolean;
isEmptyDevice: boolean;
portfolioContentRef: React.MutableRefObject<PortfolioGraphRef | null>;
}) => {
const [isRefreshing, setIsRefreshing] = useState(false);
Expand All @@ -34,7 +34,7 @@ export const useHomeRefreshControl = ({
}, [dispatch, portfolioContentRef]);

const refreshControl = useMemo(() => {
if (isPortfolioEmpty) return undefined;
if (isEmptyDevice) return undefined;

return (
<RefreshControl
Expand All @@ -43,7 +43,7 @@ export const useHomeRefreshControl = ({
colors={[colors.backgroundPrimaryDefault]}
/>
);
}, [isPortfolioEmpty, handleRefresh, colors, isRefreshing]);
}, [isEmptyDevice, handleRefresh, colors, isRefreshing]);

return refreshControl;
};

0 comments on commit b6ccc94

Please sign in to comment.