diff --git a/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/Stake/DelegatorStake.tsx b/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/Stake/DelegatorStake.tsx index 1e1a5068..3c9649b5 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/Stake/DelegatorStake.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/Stake/DelegatorStake.tsx @@ -26,7 +26,7 @@ import { useGetTransactionFee } from '@popup/shared/utils/transaction-helpers'; import FullscreenNotice, { FullscreenNoticeProps } from '@popup/popupX/shared/FullscreenNotice'; import { DelegationTypeForm, DelegatorForm, DelegatorStakeForm, configureDelegatorPayloadFromForm } from '../util'; -import {isAboveStakeWarningThreshold} from '../../util'; +import { STAKE_WARNING_THRESHOLD, isAboveStakeWarningThreshold } from '../../util'; type PoolInfoProps = { /** The validator pool ID to show information for */ @@ -72,15 +72,16 @@ type HighStakeNoticeProps = FullscreenNoticeProps & { onContinue(): void; }; -function HighStakeNotice({ onContinue, ...props }: HighStakeNoticeProps) { +function HighStakeWarning({ onContinue, ...props }: HighStakeNoticeProps) { + const { t } = useTranslation('x', { keyPrefix: 'earn.delegator.stake.overStakeThresholdWarning' }); return ( - - Notice text... + + {t('description', { threshold: STAKE_WARNING_THRESHOLD.toString() })} - - + + @@ -171,7 +172,7 @@ export default function DelegatorStake({ title, target, initialValues, existingV return ( <> - setHighStakeWarning(false)} onContinue={submit} /> + setHighStakeWarning(false)} onContinue={submit} /> diff --git a/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/TransactionFlow.tsx b/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/TransactionFlow.tsx index 8d3fd938..ec03f74d 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/TransactionFlow.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/Delegator/TransactionFlow.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/destructuring-assignment */ import React, { useCallback, useState } from 'react'; import { Location, Navigate, useLocation, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -7,19 +8,38 @@ import { absoluteRoutes } from '@popup/popupX/constants/routes'; import MultiStepForm from '@popup/shared/MultiStepForm'; import { useSelectedAccountInfo } from '@popup/shared/AccountInfoListenerContext/AccountInfoListenerContext'; import { formatCcdAmount } from '@popup/popupX/shared/utils/helpers'; +import FullscreenNotice, { FullscreenNoticeProps } from '@popup/popupX/shared/FullscreenNotice'; +import Page from '@popup/popupX/shared/Page'; +import Button from '@popup/popupX/shared/Button'; import DelegatorStake from './Stake'; import DelegatorType from './Type'; import { configureDelegatorPayloadFromForm, type DelegatorForm } from './util'; import { DelegationResultLocationState } from './Result/DelegationResult'; +function NoChangesNotice(props: FullscreenNoticeProps) { + const { t } = useTranslation('x', { keyPrefix: 'earn.delegator.update.noChangesNotice' }); + return ( + + + + {t('description')} + + + + + + ); +} + type Props = { + title: string; existingValues?: DelegatorForm | undefined; }; -export default function DelegatorTransactionFlow({ existingValues }: Props) { +function DelegatorTransactionFlow({ existingValues, title }: Props) { const { state, pathname } = useLocation() as Location & { state: DelegatorForm | undefined }; - const { t } = useTranslation('x', { keyPrefix: 'earn.delegator.register' }); const nav = useNavigate(); + const [noChangesNotice, setNoChangesNotice] = useState(false); const initialValues = state ?? existingValues; const store = useState>(initialValues ?? {}); @@ -28,45 +48,59 @@ export default function DelegatorTransactionFlow({ existingValues }: Props) { (form: DelegatorForm) => { const payload = configureDelegatorPayloadFromForm(form, existingValues); + if (Object.values(payload).every((v) => v === undefined)) { + setNoChangesNotice(true); + return; + } + nav(pathname, { replace: true, state: form }); // Override current router entry with stateful version const submitDelegatorState: DelegationResultLocationState = { payload, type: 'register' }; nav(absoluteRoutes.settings.earn.delegator.submit.path, { state: submitDelegatorState }); }, - [pathname, existingValues] + [pathname, existingValues, setNoChangesNotice] ); return ( - onDone={handleDone} valueStore={store}> - {{ - target: { - render: (initial, onNext) => ( - - ), - }, - stake: { - render: (initial, onNext, form) => { - if (form.target === undefined) { - return ; - } - - return ( - - ); + <> + setNoChangesNotice(false)} /> + onDone={handleDone} valueStore={store}> + {{ + target: { + render: (initial, onNext) => ( + + ), }, - }, - }} - + stake: { + render: (initial, onNext, form) => { + if (form.target === undefined) { + return ; + } + + return ( + + ); + }, + }, + }} + + ); } +export function RegisterDelegatorTransactionFlow() { + const { t } = useTranslation('x', { keyPrefix: 'earn.delegator.register' }); + return ; +} + export function UpdateDelegatorTransactionFlow() { + const { t } = useTranslation('x', { keyPrefix: 'earn.delegator.update' }); const accountInfo = useSelectedAccountInfo(); if (accountInfo === undefined || accountInfo.type !== AccountInfoType.Delegator) { @@ -87,5 +121,5 @@ export function UpdateDelegatorTransactionFlow() { : { type: DelegationTargetType.Baker, bakerId: delegationTarget.bakerId.toString() }, }; - return ; + return ; } diff --git a/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/i18n/en.ts b/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/i18n/en.ts index e77c2d8e..c40b23b6 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/i18n/en.ts +++ b/packages/browser-wallet/src/popup/popupX/pages/EarningRewards/i18n/en.ts @@ -69,7 +69,14 @@ const t = { backTitle: 'Earning rewards', notice: 'This will lock your delegation amount. Amount is released after {{cooldown}} days from the time you remove or decrease your delegation.', }, - update: { title: 'Update delegation' }, + update: { + title: 'Update delegation', + noChangesNotice: { + title: 'No changes', + description: 'The proposed transaction contains no changes compared to the current delegation.', + buttonBack: 'Go back', + }, + }, target: { description: 'You can delegate to an open pool of your choice, or you can stake using passive delegation.', radioValidatorLabel: 'Validator', @@ -115,6 +122,13 @@ const t = { description: 'I want to automatically add my delegation rewards to my delegation amount.', }, buttonContinue: 'Continue', + overStakeThresholdWarning: { + title: 'Important', + description: + 'You are about to lock more than {{ threshold }}% of your total balance in a delegation stake.\n\nIf you don’t have enough unlocked CCD at your disposal, you might not be able to pay future transaction fees.', + buttonContinue: 'Continue', + buttonBack: 'Enter new stake', + }, }, submit: { backTitle: 'Delegation settings', diff --git a/packages/browser-wallet/src/popup/popupX/shell/Routes.tsx b/packages/browser-wallet/src/popup/popupX/shell/Routes.tsx index b7dc309b..1db811f4 100644 --- a/packages/browser-wallet/src/popup/popupX/shell/Routes.tsx +++ b/packages/browser-wallet/src/popup/popupX/shell/Routes.tsx @@ -32,7 +32,10 @@ import { ManageTokenList, AddToken } from '@popup/popupX/pages/ManageTokens'; import { DelegationResult } from '../pages/EarningRewards/Delegator/Result'; import SubmittedTransaction from '../pages/SubmittedTransaction'; import { DelegatorIntro } from '../pages/EarningRewards/Delegator/Intro'; -import DelegatorTransactionFlow, { UpdateDelegatorTransactionFlow } from '../pages/EarningRewards/Delegator/TransactionFlow'; +import { + RegisterDelegatorTransactionFlow, + UpdateDelegatorTransactionFlow, +} from '../pages/EarningRewards/Delegator/TransactionFlow'; import DelegatorStatus from '../pages/EarningRewards/Delegator/Status'; export default function Routes({ messagePromptHandlers }: { messagePromptHandlers: MessagePromptHandlersType }) { @@ -123,7 +126,7 @@ export default function Routes({ messagePromptHandlers }: { messagePromptHandler /> } + element={} />