Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Solana on mobile via message system feature #15700

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Message, Category } from '@suite-common/suite-types';
import { ContextDomain, FeatureDomain, MessageSystemRootState } from './messageSystemTypes';

// Create app-specific selectors with correct types
const createMemoizedSelector = createWeakMapSelector.withTypes<MessageSystemRootState>();
export const createMemoizedSelector = createWeakMapSelector.withTypes<MessageSystemRootState>();

// Basic selectors don't need memoization
export const selectMessageSystemConfig = (state: MessageSystemRootState) =>
Expand Down
1 change: 1 addition & 0 deletions suite-common/message-system/src/messageSystemTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const Feature = {
ethClaim: 'eth.staking.claim',
firmwareRevisionCheck: 'security.firmware.check',
firmwareHashCheck: 'security.firmware.hashCheck',
solanaMobile: 'mobile.solana',
} as const;

export type FeatureDomain = (typeof Feature)[keyof typeof Feature];
Expand Down
4 changes: 2 additions & 2 deletions suite-native/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { selectIsAppReady, selectIsConnectInitialized, StoreProvider } from '@suite-native/state';
import { FormatterProvider } from '@suite-common/formatters';
import { NavigationContainerWithAnalytics } from '@suite-native/navigation';
import { FeatureMessageScreen, MessageSystemBannerRenderer } from '@suite-native/message-system';
import { KillswitchMessageScreen, MessageSystemBannerRenderer } from '@suite-native/message-system';
import { configureNetInfo, OfflineBanner } from '@suite-native/connection-status';
import { IntlProvider } from '@suite-native/intl';
import { isDebugEnv } from '@suite-native/config';
Expand Down Expand Up @@ -71,7 +71,7 @@ const AppComponent = () => {
</BottomSheetModalProvider>
<ModalsRenderer />
{/* NOTE: Rendered as last item so that it covers the whole app screen */}
<FeatureMessageScreen />
<KillswitchMessageScreen />
</FormatterProvider>
);
};
Expand Down
23 changes: 19 additions & 4 deletions suite-native/discovery/src/discoveryConfigSlice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { A, F, pipe } from '@mobily/ts-belt';

import {
Feature,
MessageSystemRootState,
selectIsFeatureEnabled,
} from '@suite-common/message-system';
import { createWeakMapSelector, returnStableArrayIfEmpty } from '@suite-common/redux-utils';
import {
AccountsRootState,
DeviceRootState,
Expand All @@ -17,7 +23,6 @@ import {
} from '@suite-native/config';
import { type NetworkSymbol } from '@suite-common/wallet-config';
import {
createSelectIsFeatureFlagEnabled,
FeatureFlag,
FeatureFlagsRootState,
selectIsFeatureFlagEnabled,
Expand All @@ -26,7 +31,6 @@ import {
selectNetworkSymbolsOfAccountsWithTokensAllowed,
TokensRootState,
} from '@suite-native/tokens';
import { createWeakMapSelector, returnStableArrayIfEmpty } from '@suite-common/redux-utils';

type DiscoveryInfo = {
startTimestamp: number;
Expand Down Expand Up @@ -97,10 +101,21 @@ export const selectDiscoveryInfo = (state: DiscoveryConfigSliceRootState) =>
state.discoveryConfig.discoveryInfo;

const createMemoizedSelector = createWeakMapSelector.withTypes<
DeviceRootState & DiscoveryConfigSliceRootState & FeatureFlagsRootState
DeviceRootState & DiscoveryConfigSliceRootState & FeatureFlagsRootState & MessageSystemRootState
>();

const selectIsSolanaEnabled = createSelectIsFeatureFlagEnabled(FeatureFlag.IsSolanaEnabled);
//delete once we finally release Solana
export const selectIsSolanaMessageFeatureEnabled = (state: MessageSystemRootState) =>
selectIsFeatureEnabled(state, Feature.solanaMobile, false);

const selectIsSolanaEnabled = createMemoizedSelector(
[
selectIsSolanaMessageFeatureEnabled,
state => selectIsFeatureFlagEnabled(state, FeatureFlag.IsSolanaEnabled),
],
(isSolanaMessageFeatureEnabled, isSolanaFeatureFlagEnabled) =>
isSolanaMessageFeatureEnabled || isSolanaFeatureFlagEnabled,
);

export const selectFeatureFlagEnabledNetworkSymbols = createMemoizedSelector(
[selectIsSolanaEnabled, selectAreTestnetsEnabled],
Expand Down
7 changes: 5 additions & 2 deletions suite-native/discovery/src/discoverySelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { TokenAddress, TokenSymbol } from '@suite-common/wallet-types';
import { isFirmwareVersionSupported } from '@suite-native/device';
import { FeatureFlagsRootState } from '@suite-native/feature-flags';
import { StaticSessionId } from '@trezor/connect';
import { MessageSystemRootState } from '@suite-common/message-system';

import {
DiscoveryConfigSliceRootState,
Expand Down Expand Up @@ -106,7 +107,8 @@ export const selectNetworksWithUnfinishedDiscovery = (
state: DeviceRootState &
AccountsRootState &
FeatureFlagsRootState &
DiscoveryConfigSliceRootState,
DiscoveryConfigSliceRootState &
MessageSystemRootState,
forcedAreTestnetsEnabled?: boolean,
availableCardanoDerivations?: AccountType[],
) => {
Expand All @@ -132,7 +134,8 @@ export const selectShouldRunDiscoveryForDevice = (
state: DeviceRootState &
AccountsRootState &
FeatureFlagsRootState &
DiscoveryConfigSliceRootState,
DiscoveryConfigSliceRootState &
MessageSystemRootState,
) => {
// no discovery for PortfolioTracker ever
const isPortfolioTrackerDevice = selectIsPortfolioTrackerDevice(state);
Expand Down
3 changes: 0 additions & 3 deletions suite-native/feature-flags/src/featureFlagsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { isDebugEnv, isDetoxTestBuild, isDevelopOrDebugEnv } from '@suite-native
export const FeatureFlag = {
IsDeviceConnectEnabled: 'isDeviceConnectEnabled',
IsCardanoSendEnabled: 'isCardanoSendEnabled',
IsSolanaSendEnabled: 'isSolanaSendEnabled',
IsRegtestEnabled: 'isRegtestEnabled',
IsSolanaEnabled: 'IsSolanaEnabled',
IsConnectPopupEnabled: 'IsConnectPopupEnabled',
Expand All @@ -22,7 +21,6 @@ export type FeatureFlagsRootState = {
export const featureFlagsInitialState: FeatureFlagsState = {
[FeatureFlag.IsDeviceConnectEnabled]: isAndroid() || isDebugEnv(),
[FeatureFlag.IsCardanoSendEnabled]: isAndroid() && isDevelopOrDebugEnv(),
[FeatureFlag.IsSolanaSendEnabled]: isAndroid() && isDevelopOrDebugEnv(),
[FeatureFlag.IsRegtestEnabled]: isDebugEnv() || isDetoxTestBuild(),
[FeatureFlag.IsSolanaEnabled]: false,
[FeatureFlag.IsConnectPopupEnabled]: isDevelopOrDebugEnv(),
Expand All @@ -31,7 +29,6 @@ export const featureFlagsInitialState: FeatureFlagsState = {
export const featureFlagsPersistedKeys: Array<keyof FeatureFlagsState> = [
FeatureFlag.IsDeviceConnectEnabled,
FeatureFlag.IsCardanoSendEnabled,
FeatureFlag.IsSolanaSendEnabled,
FeatureFlag.IsRegtestEnabled,
FeatureFlag.IsSolanaEnabled,
FeatureFlag.IsConnectPopupEnabled,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useDispatch, useSelector } from 'react-redux';

import { A, G } from '@mobily/ts-belt';
import { G } from '@mobily/ts-belt';

import { Box, Button, PictogramTitleHeader, VStack } from '@suite-native/atoms';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';
import { messageSystemActions, selectActiveFeatureMessages } from '@suite-common/message-system';
import { messageSystemActions } from '@suite-common/message-system';
import { Translation } from '@suite-native/intl';
import { useOpenLink } from '@suite-native/link';

import { selectActiveKillswitchMessage } from '../messageSystemSelectors';

const screenStyle = prepareNativeStyle(utils => ({
flexGrow: 1,
justifyContent: 'center',
Expand All @@ -32,31 +34,24 @@ const buttonsWrapperStyle = prepareNativeStyle(_ => ({
width: '100%',
}));

export const FeatureMessageScreen = () => {
export const KillswitchMessageScreen = () => {
const dispatch = useDispatch();
const openLink = useOpenLink();

const activeFeatureMessages = useSelector(selectActiveFeatureMessages);
const firstFeatureMessage = A.head(activeFeatureMessages);

const { applyStyle } = useNativeStyles();

if (!firstFeatureMessage) return null;
const killswitch = useSelector(selectActiveKillswitchMessage);

if (!killswitch) return null;

const {
id: messageId,
variant,
headline,
content,
cta,
dismissible: isDismissable,
dismissible: isDismissible,
category,
feature,
} = firstFeatureMessage;

const isKillswitch = A.isNotEmpty(
feature?.filter(item => item.domain === 'killswitch' && item.flag) ?? [],
);
} = killswitch;

// TODO: We use only English locale in suite-native so far. When the localization to other
// languages is implemented, the language selection logic has to be added here.
Expand All @@ -77,7 +72,7 @@ export const FeatureMessageScreen = () => {
const isCtaVisible = ctaLabel && ctaLink;

const handleDismiss = () => {
if (!isDismissable) return;
if (!isDismissible) return;

const categories = G.isArray(category) ? category : [category];
categories.forEach(item => {
Expand All @@ -90,20 +85,15 @@ export const FeatureMessageScreen = () => {
});
};

const defaultTitle = isKillswitch ? (
<Translation id="messageSystem.killswitch.title" />
) : undefined;
const defaultContent = isKillswitch ? (
<Translation id="messageSystem.killswitch.content" />
) : undefined;

return (
<Box style={applyStyle(screenStyle)}>
<Box style={applyStyle(contentStyle)}>
<PictogramTitleHeader
variant={variant}
title={messageTitle ?? defaultTitle}
subtitle={messageContent ?? defaultContent}
title={messageTitle ?? <Translation id="messageSystem.killswitch.title" />}
subtitle={
messageContent ?? <Translation id="messageSystem.killswitch.content" />
}
titleVariant="titleMedium"
/>
</Box>
Expand All @@ -113,7 +103,7 @@ export const FeatureMessageScreen = () => {
{ctaLabel}
</Button>
)}
{isDismissable && (
{isDismissible && (
<Button size="large" colorScheme="tertiaryElevation0" onPress={handleDismiss}>
<Translation id="generic.buttons.dismiss" />
</Button>
Expand Down
3 changes: 2 additions & 1 deletion suite-native/message-system/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './messageSystemMiddleware';
export * from './components/MessageSystemBannerRenderer';
export * from './components/FeatureMessageScreen';
export * from './components/KillswitchMessageScreen';
export * from './messageSystemSelectors';
17 changes: 17 additions & 0 deletions suite-native/message-system/src/messageSystemSelectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { A } from '@mobily/ts-belt';

import { createMemoizedSelector, selectActiveFeatureMessages } from '@suite-common/message-system';

export const selectActiveKillswitchMessage = createMemoizedSelector(
[selectActiveFeatureMessages],
messages =>
A.head(
messages.filter(m => {
const killswitchFeatures = m.feature?.filter(
item => item.domain === 'killswitch' && item?.flag,
);

return A.isNotEmpty(killswitchFeatures ?? []);
}),
),
);
23 changes: 2 additions & 21 deletions suite-native/module-accounts-management/src/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,23 @@
import { D, pipe } from '@mobily/ts-belt';

import { type NetworkSymbol, getNetworkType, networks } from '@suite-common/wallet-config';
import { type NetworkSymbol, getNetworkType } from '@suite-common/wallet-config';
import {
FeatureFlagsRootState,
selectIsFeatureFlagEnabled,
FeatureFlag,
} from '@suite-native/feature-flags';

const PRODUCTION_SEND_COINS_WHITELIST = pipe(
networks,
D.filter(
network =>
network.networkType === 'bitcoin' ||
network.networkType === 'ethereum' ||
network.networkType === 'ripple',
),
D.keys,
);

export const selectIsNetworkSendFlowEnabled = (
state: FeatureFlagsRootState,
symbol?: NetworkSymbol,
) => {
if (!symbol) return false;
const networkType = getNetworkType(symbol);

if (PRODUCTION_SEND_COINS_WHITELIST.includes(symbol)) return true;

const isCardanoSendEnabled = selectIsFeatureFlagEnabled(
state,
FeatureFlag.IsCardanoSendEnabled,
);

if (isCardanoSendEnabled && networkType === 'cardano') return true;

const isSolanaSendEnabled = selectIsFeatureFlagEnabled(state, FeatureFlag.IsSolanaSendEnabled);

if (isSolanaSendEnabled && networkType === 'solana') return true;
if (networkType !== 'cardano' || isCardanoSendEnabled) return true;

Copy link
Contributor Author

@vytick vytick Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

send for all the other networkTypes is already enabled, and we will enable sending solana at the same time as we start supporting it. So there is no need to check anything else but cardano FF

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense to simplify that 👍

return false;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { FeatureFlag as FeatureFlagEnum, useFeatureFlag } from '@suite-native/fe
const featureFlagsTitleMap = {
[FeatureFlagEnum.IsDeviceConnectEnabled]: 'Connect device',
[FeatureFlagEnum.IsCardanoSendEnabled]: 'Cardano send',
[FeatureFlagEnum.IsSolanaSendEnabled]: 'Solana send',
[FeatureFlagEnum.IsRegtestEnabled]: 'Regtest',
[FeatureFlagEnum.IsSolanaEnabled]: 'Solana',
[FeatureFlagEnum.IsConnectPopupEnabled]: 'Connect Popup',
Expand Down
Loading