diff --git a/src/assets/CreateNewWalletGroup.png b/src/assets/CreateNewWalletGroup.png
new file mode 100644
index 00000000000..13a91c373e5
Binary files /dev/null and b/src/assets/CreateNewWalletGroup.png differ
diff --git a/src/components/sheet/SlackSheet.tsx b/src/components/sheet/SlackSheet.tsx
index 27212d0527c..0fc473c1835 100644
--- a/src/components/sheet/SlackSheet.tsx
+++ b/src/components/sheet/SlackSheet.tsx
@@ -45,7 +45,8 @@ const Container = styled(Centered).attrs({
? deviceHeight - contentHeight
: 0,
}),
- ...(IS_ANDROID ? { borderTopLeftRadius: 30, borderTopRightRadius: 30 } : {}),
+ borderTopLeftRadius: 30,
+ borderTopRightRadius: 30,
backgroundColor: backgroundColor,
bottom: 0,
left: 0,
diff --git a/src/languages/en_US.json b/src/languages/en_US.json
index 5865734fece..c7678568df5 100644
--- a/src/languages/en_US.json
+++ b/src/languages/en_US.json
@@ -2650,6 +2650,14 @@
"my_account_address": "My account address:",
"network_title": "Network",
"new": {
+ "choose_wallet_group": {
+ "title": "Choose Wallet Group",
+ "description": "Choose the wallet group you’d like to create a new wallet within, or create a new one."
+ },
+ "new_wallet_group": {
+ "title": "New Wallet Group",
+ "description": "Create a new wallet group"
+ },
"add_wallet_sheet": {
"options": {
"cloud": {
diff --git a/src/navigation/AddWalletNavigator.tsx b/src/navigation/AddWalletNavigator.tsx
index 30b617d7bbf..92b6cb7bc7c 100644
--- a/src/navigation/AddWalletNavigator.tsx
+++ b/src/navigation/AddWalletNavigator.tsx
@@ -7,6 +7,7 @@ import { ImportOrWatchWalletSheet, ImportOrWatchWalletSheetParams } from '@/scre
import { BackgroundProvider } from '@/design-system';
import { RouteProp, useRoute } from '@react-navigation/native';
import { SimpleSheet } from '@/components/sheet/SimpleSheet';
+import { ChooseWalletGroup } from './ChooseWalletGroup';
const Swipe = createMaterialTopTabNavigator();
diff --git a/src/navigation/ChooseWalletGroup.tsx b/src/navigation/ChooseWalletGroup.tsx
new file mode 100644
index 00000000000..d768d071b23
--- /dev/null
+++ b/src/navigation/ChooseWalletGroup.tsx
@@ -0,0 +1,240 @@
+import React from 'react';
+import { Separator, Text, useBackgroundColor, useForegroundColor } from '@/design-system';
+import { View, Text as NativeText } from 'react-native';
+import chroma from 'chroma-js';
+import { useInitializeWallet, useWallets } from '@/hooks';
+import { PROFILES, useExperimentalFlag } from '@/config';
+import { useDispatch } from 'react-redux';
+import Routes from '@/navigation/routesNames';
+import { useNavigation } from './Navigation';
+import WalletBackupTypes from '@/helpers/walletBackupTypes';
+import WalletTypes from '@/helpers/walletTypes';
+import { backupUserDataIntoCloud } from '@/handlers/cloudBackup';
+import { logger, RainbowError } from '@/logger';
+import { createAccountForWallet, walletsLoadState } from '@/redux/wallets';
+import { createWallet, RainbowAccount, RainbowWallet } from '@/model/wallet';
+import { ButtonPressAnimation } from '@/components/animations';
+import { abbreviateEnsForDisplay, formatAddressForDisplay } from '@/utils/abbreviations';
+import { ImgixImage } from '@/components/images';
+import { useTheme } from '@/theme';
+import { removeFirstEmojiFromString, returnStringFirstEmoji } from '@/helpers/emojiHandler';
+import { profileUtils } from '@/utils';
+import * as i18n from '@/languages';
+import showWalletErrorAlert from '@/helpers/support';
+import { ScrollView } from 'react-native-gesture-handler';
+import CreateNewWalletGroupIcon from '@/assets/CreateNewWalletGroup.png';
+import { SlackSheet } from '@/components/sheet';
+
+function NewWalletGroup({ numWalletGroups }: { numWalletGroups: number }) {
+ const blue = useForegroundColor('blue');
+
+ const { navigate } = useNavigation();
+ const profilesEnabled = useExperimentalFlag(PROFILES);
+ const dispatch = useDispatch();
+ const initializeWallet = useInitializeWallet();
+
+ const onNewWalletGroup = () => {
+ navigate(Routes.MODAL_SCREEN, {
+ actionType: 'Create',
+ numWalletGroups,
+ onCloseModal: async (args: { name: string; color: number }) => {
+ if (!args) return;
+ try {
+ const { name, color } = args;
+ await createWallet({ color, name });
+ await dispatch(walletsLoadState(profilesEnabled));
+ // @ts-ignore
+ await initializeWallet();
+ navigate(Routes.WALLET_SCREEN, {}, true);
+ } catch (error) {
+ logger.error(new RainbowError('[AddWalletSheet]: Error while trying to add account'), { error });
+ }
+ },
+ profile: { color: null, name: `` },
+ type: 'new_wallet_group',
+ });
+ };
+
+ return (
+
+
+
+
+ {i18n.t(i18n.l.wallet.new.new_wallet_group.title)}
+
+
+ {i18n.t(i18n.l.wallet.new.new_wallet_group.description)}
+
+
+
+ );
+}
+
+function AccountAvatar({ account, size }: { account: RainbowAccount; size: number }) {
+ const { colors } = useTheme();
+
+ if (account.image) {
+ return ;
+ }
+
+ const backgroundColor = colors.avatarBackgrounds[account.color];
+ const emoji = returnStringFirstEmoji(account.label) || profileUtils.addressHashedEmoji(account.address);
+
+ return (
+
+ {emoji}
+
+ );
+}
+
+function WalletGroup({ wallet }: { wallet: RainbowWallet }) {
+ const separatorSecondary = useForegroundColor('separatorSecondary');
+
+ const accounts = wallet.addresses;
+
+ const { navigate } = useNavigation();
+ const dispatch = useDispatch();
+ const initializeWallet = useInitializeWallet();
+
+ const onAddToGroup = () => {
+ navigate(Routes.MODAL_SCREEN, {
+ actionType: 'Create',
+ asset: [],
+ isNewProfile: true,
+ onCloseModal: async (args: { name: string; color: number }) => {
+ if (!args) return;
+ try {
+ const { name, color } = args;
+ if (wallet.damaged) throw new Error('Wallet is damaged');
+ const newWallets = await dispatch(createAccountForWallet(wallet.id, color, name));
+ // @ts-ignore
+ await initializeWallet();
+ // If this wallet was previously backed up to the cloud
+ // We need to update userData backup so it can be restored too
+ if (wallet.backedUp && wallet.backupType === WalletBackupTypes.cloud) {
+ try {
+ await backupUserDataIntoCloud({ wallets: newWallets });
+ } catch (error) {
+ logger.error(new RainbowError('[AddWalletSheet]: Updating wallet userdata failed after new account creation'), { error });
+ throw error;
+ }
+ }
+ navigate(Routes.WALLET_SCREEN, {}, true);
+ } catch (e) {
+ logger.error(new RainbowError('[AddWalletSheet]: Error while trying to add account'), {
+ error: e,
+ });
+ showWalletErrorAlert();
+ }
+ },
+ profile: { color: null, name: `` },
+ type: 'wallet_profile',
+ });
+ };
+
+ return (
+
+
+ {accounts.slice(0, 4).map(account => (
+
+ ))}
+
+
+
+ {removeFirstEmojiFromString(wallet.name)}
+
+
+
+ {abbreviateEnsForDisplay(accounts[0].ens) || formatAddressForDisplay(accounts[0].address, 4, 4)}
+
+ {accounts.length > 1 && (
+
+
+ {`+${accounts.length - 1}`}
+
+
+ )}
+
+
+
+ );
+}
+
+export function ChooseWalletGroup() {
+ const { goBack } = useNavigation();
+ const { wallets } = useWallets();
+ const surfaceSecondary = useBackgroundColor('surfaceSecondary');
+
+ if (!wallets) {
+ showWalletErrorAlert();
+ return;
+ }
+
+ const groups = Object.values(wallets).filter(wallet => wallet.type === WalletTypes.mnemonic);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {i18n.t(i18n.l.wallet.new.choose_wallet_group.title)}
+
+
+ {i18n.t(i18n.l.wallet.new.choose_wallet_group.description)}
+
+
+
+
+
+
+
+ {groups.length > 0 && (
+
+ {groups.map(wallet => (
+
+ ))}
+
+ )}
+
+
+ );
+}
diff --git a/src/navigation/Routes.android.tsx b/src/navigation/Routes.android.tsx
index 1e03c2d96a3..3123532bbec 100644
--- a/src/navigation/Routes.android.tsx
+++ b/src/navigation/Routes.android.tsx
@@ -37,6 +37,7 @@ import {
stackNavigationConfig,
learnWebViewScreenConfig,
backupSheetSizes,
+ panelConfig,
} from './config';
import {
addWalletNavigatorPreset,
@@ -93,6 +94,7 @@ import { RootStackParamList } from './types';
import WalletLoadingListener from '@/components/WalletLoadingListener';
import { Portal as CMPortal } from '@/react-native-cool-modals/Portal';
import { NetworkSelector } from '@/components/NetworkSwitcher';
+import { ChooseWalletGroup } from './ChooseWalletGroup';
const Stack = createStackNavigator();
const OuterStack = createStackNavigator();
@@ -161,6 +163,7 @@ function BSNavigator() {
+
+
{profilesEnabled && (
<>
diff --git a/src/navigation/routesNames.ts b/src/navigation/routesNames.ts
index ff4372906e3..3615eede1e2 100644
--- a/src/navigation/routesNames.ts
+++ b/src/navigation/routesNames.ts
@@ -4,6 +4,7 @@ const Routes = {
ADD_CASH_SCREEN_NAVIGATOR: 'AddCashSheetNavigator',
ADD_CASH_SHEET: 'AddCashSheet',
ADD_WALLET_NAVIGATOR: 'AddWalletNavigator',
+ CHOOSE_WALLET_GROUP: 'ChooseWalletGroup',
ADD_WALLET_SHEET: 'AddWalletSheet',
APP_ICON_UNLOCK_SHEET: 'AppIconUnlockSheet',
AVATAR_BUILDER: 'AvatarBuilder',
diff --git a/src/screens/AddWalletSheet.tsx b/src/screens/AddWalletSheet.tsx
index 92712d4241d..b0d4450cb22 100644
--- a/src/screens/AddWalletSheet.tsx
+++ b/src/screens/AddWalletSheet.tsx
@@ -3,28 +3,20 @@ import { AddWalletItem } from '@/components/add-wallet/AddWalletRow';
import { Box, globalColors, Inset } from '@/design-system';
import { useNavigation } from '@/navigation';
import Routes from '@/navigation/routesNames';
-import React, { useRef } from 'react';
+import React from 'react';
import * as i18n from '@/languages';
import { HARDWARE_WALLETS, useExperimentalFlag } from '@/config';
import { analytics, analyticsV2 } from '@/analytics';
import { InteractionManager } from 'react-native';
-import { createAccountForWallet, walletsLoadState } from '@/redux/wallets';
-import { createWallet } from '@/model/wallet';
-import WalletTypes from '@/helpers/walletTypes';
import { logger, RainbowError } from '@/logger';
import WalletsAndBackup from '@/assets/WalletsAndBackup.png';
import CreateNewWallet from '@/assets/CreateNewWallet.png';
import PairHairwareWallet from '@/assets/PairHardwareWallet.png';
import ImportSecretPhraseOrPrivateKey from '@/assets/ImportSecretPhraseOrPrivateKey.png';
import WatchWalletIcon from '@/assets/watchWallet.png';
-import { useDispatch } from 'react-redux';
-import showWalletErrorAlert from '@/helpers/support';
import { cloudPlatform } from '@/utils/platform';
import { RouteProp, useRoute } from '@react-navigation/native';
-import { useInitializeWallet, useWallets } from '@/hooks';
-import { WalletLoadingStates } from '@/helpers/walletLoadingStates';
import { executeFnIfCloudBackupAvailable } from '@/model/backup';
-import { walletLoadingStore } from '@/state/walletLoading/walletLoading';
const TRANSLATIONS = i18n.l.wallet.new.add_wallet_sheet;
@@ -44,10 +36,6 @@ export const AddWalletSheet = () => {
const { goBack, navigate } = useNavigation();
const hardwareWalletsEnabled = useExperimentalFlag(HARDWARE_WALLETS);
- const dispatch = useDispatch();
- const initializeWallet = useInitializeWallet();
- const creatingWallet = useRef();
- const { isDamaged, selectedWallet, wallets } = useWallets();
const onPressCreate = async () => {
try {
@@ -56,97 +44,8 @@ export const AddWalletSheet = () => {
type: 'new',
});
analytics.track('Tapped "Create a new wallet"');
- if (creatingWallet.current) return;
- creatingWallet.current = true;
- // Show naming modal
- InteractionManager.runAfterInteractions(() => {
- goBack();
- });
- InteractionManager.runAfterInteractions(() => {
- setTimeout(() => {
- navigate(Routes.MODAL_SCREEN, {
- actionType: 'Create',
- asset: [],
- isNewProfile: true,
- onCancel: () => {
- creatingWallet.current = false;
- },
- onCloseModal: async (args: any) => {
- if (args) {
- walletLoadingStore.setState({
- loadingState: WalletLoadingStates.CREATING_WALLET,
- });
-
- const name = args?.name ?? '';
- const color = args?.color ?? null;
- // Check if the selected wallet is the primary
- let primaryWalletKey = selectedWallet.primary ? selectedWallet.id : null;
-
- // If it's not, then find it
- !primaryWalletKey &&
- Object.keys(wallets as any).some(key => {
- const wallet = wallets?.[key];
- if (wallet?.type === WalletTypes.mnemonic && wallet.primary) {
- primaryWalletKey = key;
- return true;
- }
- return false;
- });
-
- // If there's no primary wallet at all,
- // we fallback to an imported one with a seed phrase
- !primaryWalletKey &&
- Object.keys(wallets as any).some(key => {
- const wallet = wallets?.[key];
- if (wallet?.type === WalletTypes.mnemonic && wallet.imported) {
- primaryWalletKey = key;
- return true;
- }
- return false;
- });
- try {
- // If we found it and it's not damaged use it to create the new account
- if (primaryWalletKey && !wallets?.[primaryWalletKey].damaged) {
- await dispatch(createAccountForWallet(primaryWalletKey, color, name));
- // @ts-ignore
- await initializeWallet();
- } else {
- // If doesn't exist, we need to create a new wallet
- await createWallet({
- color,
- name,
- clearCallbackOnStartCreation: true,
- });
- await dispatch(walletsLoadState());
- // @ts-expect-error - needs refactor to object params
- await initializeWallet();
- }
- } catch (e) {
- logger.error(new RainbowError('[AddWalletSheet]: Error while trying to add account'), {
- error: e,
- });
- if (isDamaged) {
- setTimeout(() => {
- showWalletErrorAlert();
- }, 1000);
- }
- } finally {
- walletLoadingStore.setState({
- loadingState: null,
- });
- }
- }
- creatingWallet.current = false;
- },
- profile: {
- color: null,
- name: ``,
- },
- type: 'wallet_profile',
- });
- }, 50);
- });
+ navigate(Routes.CHOOSE_WALLET_GROUP, {});
} catch (e) {
logger.error(new RainbowError('[AddWalletSheet]: Error while trying to add account'), {
error: e,
diff --git a/src/utils/abbreviations.ts b/src/utils/abbreviations.ts
index 4b595f4714a..663043e530d 100644
--- a/src/utils/abbreviations.ts
+++ b/src/utils/abbreviations.ts
@@ -16,7 +16,7 @@ export function formatAddressForDisplay(text: string, truncationLength = 4, firs
return isValidDomainFormat(text) ? text : address(text, truncationLength, firstSectionLength);
}
-export function abbreviateEnsForDisplay(text: string, truncationLength = 20, truncationLengthBuffer = 2): string | null {
+export function abbreviateEnsForDisplay(text: string | undefined, truncationLength = 20, truncationLengthBuffer = 2): string | undefined {
if (typeof text !== 'string' || !isValidDomainFormat(text)) {
return text;
}