Skip to content

Commit b8119df

Browse files
authored
feat(staking): [LW-8837] add portfolio persistence one-time message (#671)
1 parent e8d00fc commit b8119df

File tree

10 files changed

+99
-6
lines changed

10 files changed

+99
-6
lines changed

apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import { ContentLayout } from '@components/Layout';
1818
import { useTranslation } from 'react-i18next';
1919
import { BrowserViewSections } from '@lib/scripts/types';
2020
import { useWalletActivities } from '@hooks/useWalletActivities';
21+
import {
22+
MULTIDELEGATION_FIRST_VISIT_LS_KEY,
23+
MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY
24+
} from '@utils/constants';
2125

2226
export const MultiDelegationStakingPopup = (): JSX.Element => {
2327
const { t } = useTranslation();
@@ -73,11 +77,14 @@ export const MultiDelegationStakingPopup = (): JSX.Element => {
7377
const { fiatCurrency } = useCurrencyStore();
7478
const { executeWithPassword } = useWalletManager();
7579
const isLoadingNetworkInfo = useWalletStore(networkInfoStatusSelector);
76-
const MULTIDELEGATION_FIRST_VISIT_LS_KEY = 'multidelegationFirstVisit';
7780
const [multidelegationFirstVisit, { updateLocalStorage: setMultidelegationFirstVisit }] = useLocalStorage(
7881
MULTIDELEGATION_FIRST_VISIT_LS_KEY,
7982
true
8083
);
84+
const [
85+
multidelegationFirstVisitSincePortfolioPersistence,
86+
{ updateLocalStorage: setMultidelegationFirstVisitSincePortfolioPersistence }
87+
] = useLocalStorage(MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY, true);
8188
const walletAddress = walletInfo.addresses?.[0].address?.toString();
8289
const analytics = useAnalyticsContext();
8390

@@ -91,6 +98,9 @@ export const MultiDelegationStakingPopup = (): JSX.Element => {
9198
analytics,
9299
multidelegationFirstVisit,
93100
triggerMultidelegationFirstVisit: () => setMultidelegationFirstVisit(false),
101+
multidelegationFirstVisitSincePortfolioPersistence,
102+
triggerMultidelegationFirstVisitSincePortfolioPersistence: () =>
103+
setMultidelegationFirstVisitSincePortfolioPersistence(false),
94104
backgroundServiceAPIContextSetWalletPassword: setWalletPassword,
95105
expandStakingView: () => handleOpenBrowser({ section: BrowserViewSections.STAKING }),
96106
balancesBalance: balance,

apps/browser-extension-wallet/src/types/local-storage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ export interface ILocalStorage {
4242
analyticsAccepted?: EnhancedAnalyticsOptInStatus;
4343
isForgotPasswordFlow?: boolean;
4444
multidelegationFirstVisit?: boolean;
45+
multidelegationFirstVisitSincePortfolioPersistence?: boolean;
4546
}

apps/browser-extension-wallet/src/utils/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,7 @@ export const APP_MODE_BROWSER: AppMode = 'browser';
103103
export const SEND_NFT_DEFAULT_AMOUNT = '1';
104104

105105
export const COINGECKO_URL = 'https://www.coingecko.com';
106+
107+
export const MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY =
108+
'multidelegationFirstVisitSincePortfolioPersistence';
109+
export const MULTIDELEGATION_FIRST_VISIT_LS_KEY = 'multidelegationFirstVisit';

apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import { usePassword, useSubmitingState } from '@views/browser/features/send-tra
1313
import { useWalletStore } from '@stores';
1414
import { compactNumberWithUnit } from '@utils/format-number';
1515
import { useWalletActivities } from '@hooks/useWalletActivities';
16-
17-
const MULTIDELEGATION_FIRST_VISIT_LS_KEY = 'multidelegationFirstVisit';
16+
import {
17+
MULTIDELEGATION_FIRST_VISIT_LS_KEY,
18+
MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY
19+
} from '@utils/constants';
1820

1921
export const MultiDelegationStaking = (): JSX.Element => {
2022
const { theme } = useTheme();
@@ -71,6 +73,10 @@ export const MultiDelegationStaking = (): JSX.Element => {
7173
MULTIDELEGATION_FIRST_VISIT_LS_KEY,
7274
true
7375
);
76+
const [
77+
multidelegationFirstVisitSincePortfolioPersistence,
78+
{ updateLocalStorage: setMultidelegationFirstVisitSincePortfolioPersistence }
79+
] = useLocalStorage(MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY, true);
7480
const walletAddress = walletInfo.addresses?.[0].address?.toString();
7581
const analytics = useAnalyticsContext();
7682

@@ -104,6 +110,9 @@ export const MultiDelegationStaking = (): JSX.Element => {
104110
compactNumber: compactNumberWithUnit,
105111
multidelegationFirstVisit,
106112
triggerMultidelegationFirstVisit: () => setMultidelegationFirstVisit(false),
113+
multidelegationFirstVisitSincePortfolioPersistence,
114+
triggerMultidelegationFirstVisitSincePortfolioPersistence: () =>
115+
setMultidelegationFirstVisitSincePortfolioPersistence(false),
107116
walletAddress,
108117
currentChain
109118
}}

packages/staking/src/features/i18n/translations/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ export const en: Translations = {
9191
'modals.beta.description':
9292
'This feature allows you to stake to up to {{maxPools}} pools. This is still in beta version, so some functionality might not be available. Read more about multi-delegation in our <Link>dedicated blog.</Link>',
9393
'modals.beta.pill': 'Beta',
94+
'modals.beta.portfolioPersistence.description':
95+
"Lace now supports on-chain portfolio persistence! This feature helps protect portfolios from significant drift and ensures cross-device syncing. If you've previously submitted a delegation, please resubmit your current (or a new) delegation to enable on-chain portfolio persistence.",
96+
'modals.beta.portfolioPersistence.title': 'Multi-delegation: Portfolio Persistence',
9497
'modals.beta.title': 'Multi-delegation',
9598
'modals.changingPreferences.buttons.cancel': 'Cancel',
9699
'modals.changingPreferences.buttons.confirm': 'Fine by me',

packages/staking/src/features/i18n/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ type KeysStructure = {
155155
title: '';
156156
description: '';
157157
button: '';
158+
portfolioPersistence: {
159+
title: '';
160+
description: '';
161+
};
158162
};
159163
poolsManagement: {
160164
title: '';
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Flex } from '@lace/ui';
2+
import { useEffect, useState } from 'react';
3+
import { useTranslation } from 'react-i18next';
4+
import { StakingModal } from './StakingModal';
5+
6+
interface PortfolioPersistenceModalProps {
7+
visible: boolean;
8+
onConfirm: () => void;
9+
popupView?: boolean;
10+
}
11+
12+
const CONFIRMATION_DELAY_IN_MS = 4000;
13+
14+
export const PortfolioPersistenceModal = ({ popupView, visible, onConfirm }: PortfolioPersistenceModalProps) => {
15+
const { t } = useTranslation();
16+
const [confirmDisabled, setConfirmDisabled] = useState(true);
17+
18+
useEffect(() => {
19+
setTimeout(() => {
20+
setConfirmDisabled(false);
21+
}, CONFIRMATION_DELAY_IN_MS);
22+
}, []);
23+
24+
return (
25+
<StakingModal
26+
announcement
27+
popupView={popupView}
28+
title={
29+
<Flex
30+
alignItems={popupView ? 'flex-start' : 'center'}
31+
flexDirection={popupView ? 'column-reverse' : 'row'}
32+
gap="$8"
33+
>
34+
{t('modals.beta.portfolioPersistence.title')}
35+
</Flex>
36+
}
37+
visible={visible}
38+
description={t('modals.beta.portfolioPersistence.description')}
39+
actions={[
40+
{
41+
body: t('modals.beta.button'),
42+
dataTestId: 'multidelegation-portfolio-persistence-modal-button',
43+
disabled: confirmDisabled,
44+
onClick: onConfirm,
45+
},
46+
]}
47+
/>
48+
);
49+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { ChangingPreferencesModal } from './ChangingPreferencesModal';
22
export { MultidelegationBetaModal } from './MultidelegationBetaModal';
33
export { PoolsManagementModal, PoolsManagementModalType } from './PoolsManagementModal';
4+
export { PortfolioPersistenceModal } from './PortfolioPersistenceModal';

packages/staking/src/features/outside-handles-provider/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export type OutsideHandlesContextValue = {
103103
compactNumber: (value: number | string, decimal?: number) => string;
104104
multidelegationFirstVisit: boolean;
105105
triggerMultidelegationFirstVisit: () => void;
106+
multidelegationFirstVisitSincePortfolioPersistence: boolean;
107+
triggerMultidelegationFirstVisitSincePortfolioPersistence: () => void;
106108
walletAddress: string;
107109
currentChain: Wallet.Cardano.ChainId;
108110
};

packages/staking/src/features/staking/StakingView.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useEffect } from 'react';
33
import { useTranslation } from 'react-i18next';
44
import { BrowsePools } from '../BrowsePools';
55
import { Drawer } from '../Drawer';
6-
import { ChangingPreferencesModal, MultidelegationBetaModal } from '../modals';
6+
import { ChangingPreferencesModal, MultidelegationBetaModal, PortfolioPersistenceModal } from '../modals';
77
import { useOutsideHandles } from '../outside-handles-provider';
88
import { Overview } from '../overview';
99
import { DrawerManagementStep, DrawerStep, useDelegationPortfolioStore } from '../store';
@@ -13,14 +13,17 @@ const stepsWithBackBtn = new Set<DrawerStep>([DrawerManagementStep.Confirmation,
1313

1414
export const StakingView = () => {
1515
const { t } = useTranslation();
16-
const { portfolioMutators } = useDelegationPortfolioStore((store) => ({
16+
const { portfolioMutators, currentPortfolio } = useDelegationPortfolioStore((store) => ({
17+
currentPortfolio: store.currentPortfolio,
1718
portfolioMutators: store.mutators,
1819
}));
1920
const {
2021
walletStoreFetchNetworkInfo: fetchNetworkInfo,
2122
walletStoreBlockchainProvider: blockchainProvider,
2223
multidelegationFirstVisit,
2324
triggerMultidelegationFirstVisit,
25+
multidelegationFirstVisitSincePortfolioPersistence,
26+
triggerMultidelegationFirstVisitSincePortfolioPersistence,
2427
currentChain,
2528
} = useOutsideHandles();
2629

@@ -48,7 +51,14 @@ export const StakingView = () => {
4851
</Navigation>
4952
<Drawer showCloseIcon showBackIcon={(step: DrawerStep): boolean => stepsWithBackBtn.has(step)} />
5053
<ChangingPreferencesModal />
51-
<MultidelegationBetaModal visible={multidelegationFirstVisit} onConfirm={triggerMultidelegationFirstVisit} />
54+
{currentPortfolio.length > 1 ? (
55+
<PortfolioPersistenceModal
56+
visible={multidelegationFirstVisitSincePortfolioPersistence}
57+
onConfirm={triggerMultidelegationFirstVisitSincePortfolioPersistence}
58+
/>
59+
) : (
60+
<MultidelegationBetaModal visible={multidelegationFirstVisit} onConfirm={triggerMultidelegationFirstVisit} />
61+
)}
5262
</>
5363
);
5464
};

0 commit comments

Comments
 (0)