mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-02-20 00:33:07 +01:00
feat(native): implemented OutOfQuotaAlert & refactored alerts
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import { Translation } from '@suite/intl';
|
||||
import { selectSelectedDevice } from '@suite-common/device';
|
||||
import {
|
||||
WithSuiteSyncQuotaManagerState,
|
||||
noQuotaLeftWarningDismissed,
|
||||
selectDeviceDismissedNoQuotaLeftWarning,
|
||||
selectLeftDeviceQuota,
|
||||
selectShouldDisplayOutOfQuotaAlert,
|
||||
} from '@suite-common/suite-sync-quota-manager';
|
||||
import { selectSelectedDevice } from '@suite-common/wallet-core';
|
||||
import { Banner, Button, IconButton } from '@trezor/components';
|
||||
import { TREZOR_SUPPORT_URL } from '@trezor/urls';
|
||||
|
||||
@@ -17,14 +15,9 @@ export const OutOfQuotaBanner = () => {
|
||||
const href = useExternalLink(TREZOR_SUPPORT_URL);
|
||||
const device = useSelector(selectSelectedDevice);
|
||||
|
||||
const alreadyDismissed = useSelector((state: WithSuiteSyncQuotaManagerState) =>
|
||||
selectDeviceDismissedNoQuotaLeftWarning(state, device?.id || ''),
|
||||
);
|
||||
const quotaLeft = useSelector((state: WithSuiteSyncQuotaManagerState) =>
|
||||
selectLeftDeviceQuota(state, device?.id || ''),
|
||||
);
|
||||
const shouldDisplay = useSelector(selectShouldDisplayOutOfQuotaAlert);
|
||||
|
||||
if (quotaLeft === undefined || quotaLeft > 0 || alreadyDismissed) return null;
|
||||
if (shouldDisplay === false) return null;
|
||||
|
||||
const handleDismiss = () => {
|
||||
if (!device || !device.id) return false;
|
||||
|
||||
@@ -36,6 +36,7 @@ export {
|
||||
selectHasDeviceAllowance,
|
||||
selectLeftDeviceQuota,
|
||||
selectDeviceDismissedNoQuotaLeftWarning,
|
||||
selectShouldDisplayOutOfQuotaAlert,
|
||||
} from './quotaManagerSelectors';
|
||||
export type { WithSuiteSyncQuotaManagerState } from './quotaManagerSelectors';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { type DeviceRootState, selectDeviceId } from '@suite-common/device';
|
||||
import { createWeakMapSelector } from '@suite-common/redux-utils';
|
||||
import { WalletDescriptor } from '@suite-common/wallet-types';
|
||||
|
||||
import { SuiteSyncQuotaManagerState } from './quotaManagerReducer';
|
||||
@@ -6,6 +8,10 @@ export type WithSuiteSyncQuotaManagerState = {
|
||||
suiteSyncQuotaManager: SuiteSyncQuotaManagerState;
|
||||
};
|
||||
|
||||
const createMemoizedSelector = createWeakMapSelector.withTypes<
|
||||
DeviceRootState & WithSuiteSyncQuotaManagerState
|
||||
>();
|
||||
|
||||
export const selectQuotaManagerBaseUrl = (state: WithSuiteSyncQuotaManagerState) =>
|
||||
state.suiteSyncQuotaManager.baseUrl;
|
||||
|
||||
@@ -28,14 +34,14 @@ export const selectOwnersAllowance = (state: WithSuiteSyncQuotaManagerState) =>
|
||||
state.suiteSyncQuotaManager.ownersAllowance;
|
||||
|
||||
export const selectLeftDeviceQuota = (state: WithSuiteSyncQuotaManagerState, deviceId: string) =>
|
||||
state.suiteSyncQuotaManager.registeredDevices.find(di => di.deviceId === deviceId)
|
||||
state.suiteSyncQuotaManager.registeredDevices.find(d => d.deviceId === deviceId)
|
||||
?.unspentStorageSize;
|
||||
|
||||
export const selectDeviceDismissedNoQuotaLeftWarning = (
|
||||
state: WithSuiteSyncQuotaManagerState,
|
||||
deviceId: string,
|
||||
) =>
|
||||
state.suiteSyncQuotaManager.registeredDevices.find(di => di.deviceId === deviceId)
|
||||
state.suiteSyncQuotaManager.registeredDevices.find(d => d.deviceId === deviceId)
|
||||
?.dismissedNoQuotaLeftWarning;
|
||||
|
||||
export const selectHasDeviceAllowance = (
|
||||
@@ -43,3 +49,13 @@ export const selectHasDeviceAllowance = (
|
||||
deviceId: string,
|
||||
walletDescriptor: WalletDescriptor,
|
||||
) => selectIsDeviceRegistered(state, deviceId) && selectHasOwnerAllowance(state, walletDescriptor);
|
||||
|
||||
export const selectShouldDisplayOutOfQuotaAlert = createMemoizedSelector(
|
||||
[
|
||||
(state: WithSuiteSyncQuotaManagerState & DeviceRootState) =>
|
||||
selectLeftDeviceQuota(state, selectDeviceId(state) ?? ''),
|
||||
(state: WithSuiteSyncQuotaManagerState & DeviceRootState) =>
|
||||
selectDeviceDismissedNoQuotaLeftWarning(state, selectDeviceId(state) ?? ''),
|
||||
],
|
||||
(quotaLeft, alreadyDismissed) => quotaLeft === 0 && !alreadyDismissed,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
|
||||
import { FullAlertBox, FullAlertBoxProps } from './FullAlertBox';
|
||||
|
||||
export const AnimatedFullAlertBox = (props: FullAlertBoxProps) => (
|
||||
<Animated.View entering={FadeIn} exiting={FadeOut}>
|
||||
<FullAlertBox {...props} />
|
||||
</Animated.View>
|
||||
);
|
||||
@@ -20,8 +20,8 @@ const containerStyle = prepareNativeStyle<Pick<FullAlertStyles, 'backgroundColor
|
||||
export type FullAlertBoxProps = {
|
||||
title: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
primaryButtonLabel?: string;
|
||||
secondaryButtonLabel?: string;
|
||||
primaryButtonLabel?: string | React.ReactNode;
|
||||
secondaryButtonLabel?: string | React.ReactNode;
|
||||
onPressPrimaryButton?: () => void;
|
||||
onPressSecondaryButton?: () => void;
|
||||
primaryButtonProps?: Partial<ButtonProps>;
|
||||
|
||||
@@ -69,6 +69,7 @@ export * from './utils';
|
||||
export * from './PriceChangeBadge';
|
||||
export * from './resetLetterSpacingOnAndroidStyle';
|
||||
export * from './FullAlertBox/FullAlertBox';
|
||||
export * from './FullAlertBox/AnimatedFullAlertBox';
|
||||
export * from './CircularSpinner';
|
||||
export * from './CardStepper/CardStepper';
|
||||
export * from './Sheet/BottomSheetModal';
|
||||
|
||||
@@ -62,6 +62,13 @@ export const messages = {
|
||||
cta: 'Create wallet backup',
|
||||
},
|
||||
},
|
||||
outOfSuiteSyncQuota: {
|
||||
title: 'Suite Sync storage is full',
|
||||
subtitle:
|
||||
'New labels will be saved locally on this phone, but not synced to your other devices.',
|
||||
cta: 'Contact support',
|
||||
dismiss: 'Dismiss',
|
||||
},
|
||||
},
|
||||
tokens: '+ Tokens',
|
||||
warning: 'Warning',
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@suite-common/message-system": "workspace:*",
|
||||
"@suite-common/redux-utils": "workspace:*",
|
||||
"@suite-common/suite-sync": "workspace:*",
|
||||
"@suite-common/suite-sync-quota-manager": "workspace:*",
|
||||
"@suite-common/wallet-config": "workspace:*",
|
||||
"@suite-common/wallet-core": "workspace:*",
|
||||
"@suite-common/wallet-types": "workspace:*",
|
||||
@@ -35,7 +36,6 @@
|
||||
"@suite-native/discovery": "workspace:*",
|
||||
"@suite-native/firmware": "workspace:*",
|
||||
"@suite-native/graph": "workspace:*",
|
||||
"@suite-native/icons": "workspace:*",
|
||||
"@suite-native/intl": "workspace:*",
|
||||
"@suite-native/link": "workspace:*",
|
||||
"@suite-native/navigation": "workspace:*",
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
||||
|
||||
import { selectDeviceId, selectDeviceUpdateFirmwareVersion } from '@suite-common/device';
|
||||
import { FullAlertBox } from '@suite-native/atoms';
|
||||
import { AnimatedFullAlertBox } from '@suite-native/atoms';
|
||||
import { Translation, useTranslate } from '@suite-native/intl';
|
||||
import {
|
||||
DeviceSettingsStackRoutes,
|
||||
@@ -59,22 +58,20 @@ export const FirmwareUpdateAlert = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Animated.View entering={FadeIn} exiting={FadeOut}>
|
||||
<FullAlertBox
|
||||
title={<Translation id="moduleHome.firmwareUpdateAlert.title" />}
|
||||
description={
|
||||
<Translation
|
||||
id="moduleHome.firmwareUpdateAlert.version"
|
||||
values={{ version: updateFirmwareVersion }}
|
||||
/>
|
||||
}
|
||||
variant="info"
|
||||
secondaryButtonLabel={translate('moduleHome.firmwareUpdateAlert.button.close')}
|
||||
onPressSecondaryButton={handleClose}
|
||||
primaryButtonLabel={translate('moduleHome.firmwareUpdateAlert.button.update')}
|
||||
onPressPrimaryButton={handleUpdateFirmware}
|
||||
marginHorizontal="sp16"
|
||||
/>
|
||||
</Animated.View>
|
||||
<AnimatedFullAlertBox
|
||||
title={<Translation id="moduleHome.firmwareUpdateAlert.title" />}
|
||||
description={
|
||||
<Translation
|
||||
id="moduleHome.firmwareUpdateAlert.version"
|
||||
values={{ version: updateFirmwareVersion }}
|
||||
/>
|
||||
}
|
||||
variant="info"
|
||||
secondaryButtonLabel={translate('moduleHome.firmwareUpdateAlert.button.close')}
|
||||
onPressSecondaryButton={handleClose}
|
||||
primaryButtonLabel={translate('moduleHome.firmwareUpdateAlert.button.update')}
|
||||
onPressPrimaryButton={handleUpdateFirmware}
|
||||
marginHorizontal="sp16"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { selectShouldDisplayOutOfQuotaAlert } from '@suite-common/suite-sync-quota-manager';
|
||||
|
||||
import { SuiteSyncKeysAlert } from './SuiteSyncKeysAlert';
|
||||
import {
|
||||
selectShouldDisplaySuiteSyncAlert,
|
||||
selectShouldDisplayUpgradeFirmwareAlert,
|
||||
} from '../homescreenSelectors';
|
||||
import { FirmwareUpdateAlert } from './FirmwareUpdateAlert';
|
||||
import { OutOfQuotaAlert } from './OutOfQuotaAlert';
|
||||
|
||||
export const HomescreenAlerts = () => {
|
||||
const shouldDisplayOutOfQuotaAlert = useSelector(selectShouldDisplayOutOfQuotaAlert);
|
||||
const shouldDisplaySuiteSyncAlert = useSelector(selectShouldDisplaySuiteSyncAlert);
|
||||
const shouldDisplayFirmwareUpdateAlert = useSelector(selectShouldDisplayUpgradeFirmwareAlert);
|
||||
|
||||
if (shouldDisplaySuiteSyncAlert) {
|
||||
return <SuiteSyncKeysAlert />;
|
||||
}
|
||||
|
||||
if (shouldDisplayFirmwareUpdateAlert) {
|
||||
return <FirmwareUpdateAlert />;
|
||||
}
|
||||
|
||||
if (shouldDisplayOutOfQuotaAlert) {
|
||||
return <OutOfQuotaAlert />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { selectSelectedDevice } from '@suite-common/device';
|
||||
import { noQuotaLeftWarningDismissed } from '@suite-common/suite-sync-quota-manager';
|
||||
import { AnimatedFullAlertBox } from '@suite-native/atoms';
|
||||
import { Translation, useTranslate } from '@suite-native/intl';
|
||||
import { SUITE_MOBILE_SUPPORT_URL, useOpenLink } from '@suite-native/link';
|
||||
|
||||
export const OutOfQuotaAlert = () => {
|
||||
const dispatch = useDispatch();
|
||||
const openLink = useOpenLink();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const device = useSelector(selectSelectedDevice);
|
||||
|
||||
if (!device?.id) return null;
|
||||
const deviceId = device.id;
|
||||
|
||||
const handleDismiss = () => {
|
||||
dispatch(noQuotaLeftWarningDismissed({ deviceId }));
|
||||
};
|
||||
|
||||
const handleContactSupport = () => {
|
||||
openLink(SUITE_MOBILE_SUPPORT_URL);
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatedFullAlertBox
|
||||
marginHorizontal="sp16"
|
||||
variant="info"
|
||||
iconName="info"
|
||||
title={<Translation id="generic.banners.outOfSuiteSyncQuota.title" />}
|
||||
description={<Translation id="generic.banners.outOfSuiteSyncQuota.subtitle" />}
|
||||
primaryButtonLabel={translate('generic.banners.outOfSuiteSyncQuota.cta')}
|
||||
onPressPrimaryButton={handleContactSupport}
|
||||
secondaryButtonLabel={translate('generic.banners.outOfSuiteSyncQuota.dismiss')}
|
||||
onPressSecondaryButton={handleDismiss}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -19,14 +19,9 @@ import {
|
||||
StackNavigationProps,
|
||||
} from '@suite-native/navigation';
|
||||
|
||||
import { FirmwareUpdateAlert } from './FirmwareUpdateAlert';
|
||||
import { HomescreenAlerts } from './HomescreenAlerts';
|
||||
import { PortfolioGraph, PortfolioGraphRef } from './PortfolioGraph';
|
||||
import { ReferralButton } from './ReferralButton';
|
||||
import { SuiteSyncKeysAlert } from './SuiteSyncKeysAlert';
|
||||
import {
|
||||
selectShouldDisplaySuiteSyncAlert,
|
||||
selectShouldDisplayUpgradeFirmwareAlert,
|
||||
} from '../homescreenSelectors';
|
||||
|
||||
export const PortfolioContent = forwardRef<PortfolioGraphRef>((_props, ref) => {
|
||||
const navigation = useNavigation<StackNavigationProps<RootStackParamList, RootStackRoutes>>();
|
||||
@@ -37,9 +32,6 @@ export const PortfolioContent = forwardRef<PortfolioGraphRef>((_props, ref) => {
|
||||
selectHasFirmwareAuthenticityCheckHardFailed,
|
||||
);
|
||||
|
||||
const shouldDisplaySuiteSyncAlert = useSelector(selectShouldDisplaySuiteSyncAlert);
|
||||
const shouldDisplayFirmwareUpdateAlert = useSelector(selectShouldDisplayUpgradeFirmwareAlert);
|
||||
|
||||
const isPortfolioTracker = useSelector(selectIsPortfolioTrackerDevice);
|
||||
|
||||
const showTransferButtons = isDeviceAuthorized && !hasDiscovery;
|
||||
@@ -58,19 +50,9 @@ export const PortfolioContent = forwardRef<PortfolioGraphRef>((_props, ref) => {
|
||||
});
|
||||
};
|
||||
|
||||
const getHomescreenAlert = () => {
|
||||
if (shouldDisplaySuiteSyncAlert) {
|
||||
return <SuiteSyncKeysAlert />;
|
||||
} else if (shouldDisplayFirmwareUpdateAlert) {
|
||||
return <FirmwareUpdateAlert />;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VStack spacing="sp32" marginTop="sp8">
|
||||
{getHomescreenAlert()}
|
||||
<HomescreenAlerts />
|
||||
<AnimatedVStack spacing="sp32" layout={LinearTransition}>
|
||||
<PortfolioGraph ref={ref} />
|
||||
<VStack spacing="sp64" marginHorizontal="sp16">
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useCallback } from 'react';
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { selectDeviceStaticSessionId, selectIsDeviceConnected } from '@suite-common/device';
|
||||
import { FullAlertBox } from '@suite-native/atoms';
|
||||
import { useTranslate } from '@suite-native/intl';
|
||||
import { AnimatedFullAlertBox } from '@suite-native/atoms';
|
||||
import { Translation } from '@suite-native/intl';
|
||||
import {
|
||||
AuthorizeDeviceStackParamList,
|
||||
AuthorizeDeviceStackRoutes,
|
||||
@@ -25,7 +24,6 @@ type NavigationProp = StackToStackCompositeNavigationProps<
|
||||
>;
|
||||
|
||||
export const SuiteSyncKeysAlert = () => {
|
||||
const { translate } = useTranslate();
|
||||
const { suiteSync } = useNativeServices();
|
||||
|
||||
const isDeviceConnected = useSelector(selectIsDeviceConnected);
|
||||
@@ -52,15 +50,13 @@ export const SuiteSyncKeysAlert = () => {
|
||||
if (!shouldDisplaySuiteSyncAlert) return null;
|
||||
|
||||
return (
|
||||
<Animated.View entering={FadeIn} exiting={FadeOut}>
|
||||
<FullAlertBox
|
||||
variant="info"
|
||||
title={translate('moduleHome.suiteSyncAlert.title')}
|
||||
description={translate('moduleHome.suiteSyncAlert.description')}
|
||||
primaryButtonLabel={translate('moduleHome.suiteSyncAlert.button')}
|
||||
onPressPrimaryButton={allowSuiteSyncForWallet}
|
||||
marginHorizontal="sp16"
|
||||
/>
|
||||
</Animated.View>
|
||||
<AnimatedFullAlertBox
|
||||
variant="info"
|
||||
title={<Translation id="moduleHome.suiteSyncAlert.title" />}
|
||||
description={<Translation id="moduleHome.suiteSyncAlert.description" />}
|
||||
primaryButtonLabel={<Translation id="moduleHome.suiteSyncAlert.button" />}
|
||||
onPressPrimaryButton={allowSuiteSyncForWallet}
|
||||
marginHorizontal="sp16"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
{
|
||||
"path": "../../suite-common/suite-sync"
|
||||
},
|
||||
{
|
||||
"path": "../../suite-common/suite-sync-quota-manager"
|
||||
},
|
||||
{
|
||||
"path": "../../suite-common/wallet-config"
|
||||
},
|
||||
@@ -35,7 +38,6 @@
|
||||
{ "path": "../discovery" },
|
||||
{ "path": "../firmware" },
|
||||
{ "path": "../graph" },
|
||||
{ "path": "../icons" },
|
||||
{ "path": "../intl" },
|
||||
{ "path": "../link" },
|
||||
{ "path": "../navigation" },
|
||||
|
||||
@@ -13327,6 +13327,7 @@ __metadata:
|
||||
"@suite-common/message-system": "workspace:*"
|
||||
"@suite-common/redux-utils": "workspace:*"
|
||||
"@suite-common/suite-sync": "workspace:*"
|
||||
"@suite-common/suite-sync-quota-manager": "workspace:*"
|
||||
"@suite-common/wallet-config": "workspace:*"
|
||||
"@suite-common/wallet-core": "workspace:*"
|
||||
"@suite-common/wallet-types": "workspace:*"
|
||||
@@ -13343,7 +13344,6 @@ __metadata:
|
||||
"@suite-native/discovery": "workspace:*"
|
||||
"@suite-native/firmware": "workspace:*"
|
||||
"@suite-native/graph": "workspace:*"
|
||||
"@suite-native/icons": "workspace:*"
|
||||
"@suite-native/intl": "workspace:*"
|
||||
"@suite-native/link": "workspace:*"
|
||||
"@suite-native/navigation": "workspace:*"
|
||||
|
||||
Reference in New Issue
Block a user