From be2b44b49e897b032f1fef1837ea9c600a16c520 Mon Sep 17 00:00:00 2001 From: refi93 Date: Sat, 28 Oct 2023 00:19:59 +0200 Subject: [PATCH 1/4] feat(staking): add rewards list to Staking Activity tab --- .../MultiDelegationStakingPopup.tsx | 3 +- .../components/MultiDelegationStaking.tsx | 3 +- .../src/features/activity/Activity.tsx | 8 ++++- .../src/features/activity/RewardsHistory.tsx | 33 +++++++++++++++++++ .../src/features/i18n/translations/en.ts | 1 + packages/staking/src/features/i18n/types.ts | 5 +++ .../outside-handles-provider/types.ts | 1 + 7 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 packages/staking/src/features/activity/RewardsHistory.tsx diff --git a/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx b/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx index eca01a83f3..5c4ff1de87 100644 --- a/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx +++ b/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx @@ -73,7 +73,7 @@ export const MultiDelegationStakingPopup = (): JSX.Element => { name: 'AnalyticsEventNames.Staking.STAKING_MULTI_DELEGATION_POPUP' }); }, []); - const { walletActivities } = useWalletActivities({ sendAnalytics }); + const { walletActivities, walletActivitiesStatus } = useWalletActivities({ sendAnalytics }); const { fiatCurrency } = useCurrencyStore(); const { executeWithPassword } = useWalletManager(); const isLoadingNetworkInfo = useWalletStore(networkInfoStatusSelector); @@ -125,6 +125,7 @@ export const MultiDelegationStakingPopup = (): JSX.Element => { walletStoreNetworkInfo: networkInfo, walletStoreBlockchainProvider: blockchainProvider, walletStoreWalletActivities: walletActivities, + walletStoreWalletActivitiesStatus: walletActivitiesStatus, // TODO: LW-7575 make compactNumber reusable and not pass it here. compactNumber: compactNumberWithUnit, walletAddress, diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx index 4e331faa23..a10194b71f 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx @@ -66,7 +66,7 @@ export const MultiDelegationStaking = (): JSX.Element => { name: 'AnalyticsEventNames.Staking.STAKING_MULTI_DELEGATION_BROWSER' }); }, []); - const { walletActivities } = useWalletActivities({ sendAnalytics }); + const { walletActivities, walletActivitiesStatus } = useWalletActivities({ sendAnalytics }); const { fiatCurrency } = useCurrencyStore(); const { executeWithPassword } = useWalletManager(); const [multidelegationFirstVisit, { updateLocalStorage: setMultidelegationFirstVisit }] = useLocalStorage( @@ -106,6 +106,7 @@ export const MultiDelegationStaking = (): JSX.Element => { walletStoreNetworkInfo: networkInfo, walletStoreBlockchainProvider: blockchainProvider, walletStoreWalletActivities: walletActivities, + walletStoreWalletActivitiesStatus: walletActivitiesStatus, // TODO: LW-7575 make compactNumber reusable and not pass it here. compactNumber: compactNumberWithUnit, multidelegationFirstVisit, diff --git a/packages/staking/src/features/activity/Activity.tsx b/packages/staking/src/features/activity/Activity.tsx index c9d20953e1..326ceac4b1 100644 --- a/packages/staking/src/features/activity/Activity.tsx +++ b/packages/staking/src/features/activity/Activity.tsx @@ -1 +1,7 @@ -export const Activity = () => <>Activity placeholder; +import { RewardsHistory } from './RewardsHistory'; + +export const Activity = () => ( + <> + + +); diff --git a/packages/staking/src/features/activity/RewardsHistory.tsx b/packages/staking/src/features/activity/RewardsHistory.tsx new file mode 100644 index 0000000000..2e52c865d3 --- /dev/null +++ b/packages/staking/src/features/activity/RewardsHistory.tsx @@ -0,0 +1,33 @@ +import { GroupedAssetActivityList } from '@lace/core'; +import { Box, Text } from '@lace/ui'; +import { Skeleton } from 'antd'; +import { StateStatus, useOutsideHandles } from 'features/outside-handles-provider'; +import { useTranslation } from 'react-i18next'; + +const LACE_APP_ID = 'lace-app'; + +export const RewardsHistory = () => { + const { t } = useTranslation(); + const { walletStoreWalletActivitiesStatus: walletActivitiesStatus, walletStoreWalletActivities: walletActivities } = + useOutsideHandles(); + const groupedRewardsActivities = walletActivities + .map((group) => ({ + ...group, + items: group.items.filter((item) => item.type === 'rewards'), + })) + .filter((group) => group.items.length > 0); + + return ( + <> + + {t('activity.rewardsHistory.title')} + + + + + + ); +}; diff --git a/packages/staking/src/features/i18n/translations/en.ts b/packages/staking/src/features/i18n/translations/en.ts index 8355763e61..770069f7c3 100644 --- a/packages/staking/src/features/i18n/translations/en.ts +++ b/packages/staking/src/features/i18n/translations/en.ts @@ -1,6 +1,7 @@ import { Translations } from '../types'; export const en: Translations = { + 'activity.rewardsHistory.title': 'History', 'browsePools.stakePoolTableBrowser.addPool': 'Add pool', 'browsePools.stakePoolTableBrowser.disabledTooltip': 'Maximum number of pools selected', 'browsePools.stakePoolTableBrowser.emptyMessage': 'No results matching your search', diff --git a/packages/staking/src/features/i18n/types.ts b/packages/staking/src/features/i18n/types.ts index 12c83ee6e1..7b9e6a09b2 100644 --- a/packages/staking/src/features/i18n/types.ts +++ b/packages/staking/src/features/i18n/types.ts @@ -15,6 +15,11 @@ type KeysStructure = { close: ''; }; }; + activity: { + rewardsHistory: { + title: ''; + }; + }; browsePools: { stakePoolTableBrowser: { searchInputPlaceholder: ''; diff --git a/packages/staking/src/features/outside-handles-provider/types.ts b/packages/staking/src/features/outside-handles-provider/types.ts index 55d301f551..60165c488b 100644 --- a/packages/staking/src/features/outside-handles-provider/types.ts +++ b/packages/staking/src/features/outside-handles-provider/types.ts @@ -70,6 +70,7 @@ export type OutsideHandlesContextValue = { walletStoreGetKeyAgentType: () => string; walletStoreInMemoryWallet: Wallet.ObservableWallet; walletStoreWalletActivities: AssetActivityListProps[]; + walletStoreWalletActivitiesStatus: StateStatus; walletStoreWalletUICardanoCoin: Wallet.CoinId; walletManagerExecuteWithPassword: ( password: string, From 483284e516def97190e3f962aabc4e51c6f518d0 Mon Sep 17 00:00:00 2001 From: refi93 Date: Fri, 3 Nov 2023 14:53:05 +0100 Subject: [PATCH 2/4] feat(staking): expose activity detail drawer in staking section to be used by staking activity --- .../stores/slices/activity-detail-slice.ts | 4 +- .../components/MultiDelegationStaking.tsx | 40 +++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts b/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts index 726edac529..83d4dd6936 100644 --- a/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts +++ b/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts @@ -89,12 +89,15 @@ const buildGetActivityDetail = walletInfo } = get(); + set({ fetchingActivityInfo: true }); + if (activityDetail.type === 'rewards') { const { activity, status, type } = activityDetail; const poolInfos = await getPoolInfos( activity.rewards.map(({ poolId }) => poolId), stakePoolProvider ); + set({ fetchingActivityInfo: false }); return { activity: { @@ -128,7 +131,6 @@ const buildGetActivityDetail = const { activity: tx, status, type, direction } = activityDetail; const walletAssets = await firstValueFrom(wallet.assetInfo$); const protocolParameters = await firstValueFrom(wallet.protocolParameters$); - set({ fetchingActivityInfo: true }); // Assets const assetIds = getTransactionAssetsId(tx.body.outputs); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx index a10194b71f..f99a17a5a9 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/MultiDelegationStaking.tsx @@ -1,5 +1,5 @@ import { OutsideHandlesProvider, Staking } from '@lace/staking'; -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { useAnalyticsContext, useBackgroundServiceAPIContext, @@ -17,6 +17,9 @@ import { MULTIDELEGATION_FIRST_VISIT_LS_KEY, MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY } from '@utils/constants'; +import { ActivityDetail } from '../../activity'; +import { Drawer, DrawerNavigation } from '@lace/common'; +import { useTranslation } from 'react-i18next'; export const MultiDelegationStaking = (): JSX.Element => { const { theme } = useTheme(); @@ -38,7 +41,9 @@ export const MultiDelegationStaking = (): JSX.Element => { fetchNetworkInfo, networkInfo, blockchainProvider, - currentChain + currentChain, + activityDetail, + resetActivityState } = useWalletStore((state) => ({ getKeyAgentType: state.getKeyAgentType, inMemoryWallet: state.inMemoryWallet, @@ -50,8 +55,11 @@ export const MultiDelegationStaking = (): JSX.Element => { fetchNetworkInfo: state.fetchNetworkInfo, blockchainProvider: state.blockchainProvider, walletInfo: state.walletInfo, - currentChain: state.currentChain + currentChain: state.currentChain, + activityDetail: state.activityDetail, + resetActivityState: state.resetActivityState })); + const { t } = useTranslation(); const sendAnalytics = useCallback(() => { // TODO implement analytics for the new flow const analytics = { @@ -80,6 +88,11 @@ export const MultiDelegationStaking = (): JSX.Element => { const walletAddress = walletInfo.addresses?.[0].address?.toString(); const analytics = useAnalyticsContext(); + // Reset current transaction details and close drawer if network (blockchainProvider) has changed + useEffect(() => { + resetActivityState(); + }, [resetActivityState, blockchainProvider]); + return ( { }} > + {/* + Note: Mounting the browser-extension activity details drawer here is just a workaround. + Ideally, the Drawer/Activity detail should be fully managed within the "Staking" component, + which contains the respective "Activity" section, but that would require moving/refactoring + large chunks of code, ATM tightly coupled with browser-extension state/logic, + to a separate package (core perhaps?). + */} + { + resetActivityState(); + }} + /> + } + > + {activityDetail && priceResult && } + ); }; From b18b9a6fd6f6482b7e2181e91082706836c4c69a Mon Sep 17 00:00:00 2001 From: refi93 Date: Fri, 3 Nov 2023 16:43:19 +0100 Subject: [PATCH 3/4] feat(staking): add "no staking activity yet" screen --- .../src/features/activity/Activity.tsx | 26 +++++++++++++++---- .../activity/NoStakingActivity.css.ts | 13 ++++++++++ .../features/activity/NoStakingActivity.tsx | 17 ++++++++++++ .../src/features/activity/RewardsHistory.tsx | 18 +++++-------- .../helpers/getGroupedRewardsHistory.ts | 9 +++++++ .../src/features/i18n/translations/en.ts | 1 + packages/staking/src/features/i18n/types.ts | 1 + packages/staking/src/features/theme/colors.ts | 3 +++ 8 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 packages/staking/src/features/activity/NoStakingActivity.css.ts create mode 100644 packages/staking/src/features/activity/NoStakingActivity.tsx create mode 100644 packages/staking/src/features/activity/helpers/getGroupedRewardsHistory.ts diff --git a/packages/staking/src/features/activity/Activity.tsx b/packages/staking/src/features/activity/Activity.tsx index 326ceac4b1..f6fb246cc4 100644 --- a/packages/staking/src/features/activity/Activity.tsx +++ b/packages/staking/src/features/activity/Activity.tsx @@ -1,7 +1,23 @@ +import { StateStatus, useOutsideHandles } from 'features/outside-handles-provider'; +import { getGroupedRewardsActivities } from './helpers/getGroupedRewardsHistory'; +import { NoStakingActivity } from './NoStakingActivity'; import { RewardsHistory } from './RewardsHistory'; -export const Activity = () => ( - <> - - -); +export const Activity = () => { + const { walletStoreWalletActivitiesStatus: walletActivitiesStatus, walletStoreWalletActivities: walletActivities } = + useOutsideHandles(); + const groupedRewardsActivities = getGroupedRewardsActivities(walletActivities); + + return ( + <> + {walletActivitiesStatus === StateStatus.LOADED && groupedRewardsActivities.length === 0 ? ( + + ) : ( + + )} + + ); +}; diff --git a/packages/staking/src/features/activity/NoStakingActivity.css.ts b/packages/staking/src/features/activity/NoStakingActivity.css.ts new file mode 100644 index 0000000000..ebbfc6e56a --- /dev/null +++ b/packages/staking/src/features/activity/NoStakingActivity.css.ts @@ -0,0 +1,13 @@ +import { style } from '@lace/ui'; +import { theme } from 'features/theme'; + +export const sadFaceIcon = style({ + height: theme.spacing.$112, + width: theme.spacing.$112, +}); + +export const noActivityText = style({ + color: theme.colors.$activityNoActivityTextColor, + fontSize: theme.fontSizes.$14, + fontWeight: theme.fontWeights.$semibold, +}); diff --git a/packages/staking/src/features/activity/NoStakingActivity.tsx b/packages/staking/src/features/activity/NoStakingActivity.tsx new file mode 100644 index 0000000000..3e68ea8617 --- /dev/null +++ b/packages/staking/src/features/activity/NoStakingActivity.tsx @@ -0,0 +1,17 @@ +import SadFaceIcon from '@lace/core/src/ui/assets/icons/sad-face.component.svg'; +import { Flex } from '@lace/ui'; +import { Typography } from 'antd'; +import { useTranslation } from 'react-i18next'; +import * as styles from './NoStakingActivity.css'; + +export const NoStakingActivity = () => { + const { t } = useTranslation(); + return ( + + + + {t('activity.rewardsHistory.noStakingActivityYet')} + + + ); +}; diff --git a/packages/staking/src/features/activity/RewardsHistory.tsx b/packages/staking/src/features/activity/RewardsHistory.tsx index 2e52c865d3..4f2a2cc45f 100644 --- a/packages/staking/src/features/activity/RewardsHistory.tsx +++ b/packages/staking/src/features/activity/RewardsHistory.tsx @@ -1,21 +1,17 @@ -import { GroupedAssetActivityList } from '@lace/core'; +import { AssetActivityListProps, GroupedAssetActivityList } from '@lace/core'; import { Box, Text } from '@lace/ui'; import { Skeleton } from 'antd'; -import { StateStatus, useOutsideHandles } from 'features/outside-handles-provider'; +import { StateStatus } from 'features/outside-handles-provider'; import { useTranslation } from 'react-i18next'; const LACE_APP_ID = 'lace-app'; -export const RewardsHistory = () => { +type RewardsHistoryProps = { + groupedRewardsActivities: AssetActivityListProps[]; + walletActivitiesStatus: StateStatus; +}; +export const RewardsHistory = ({ groupedRewardsActivities, walletActivitiesStatus }: RewardsHistoryProps) => { const { t } = useTranslation(); - const { walletStoreWalletActivitiesStatus: walletActivitiesStatus, walletStoreWalletActivities: walletActivities } = - useOutsideHandles(); - const groupedRewardsActivities = walletActivities - .map((group) => ({ - ...group, - items: group.items.filter((item) => item.type === 'rewards'), - })) - .filter((group) => group.items.length > 0); return ( <> diff --git a/packages/staking/src/features/activity/helpers/getGroupedRewardsHistory.ts b/packages/staking/src/features/activity/helpers/getGroupedRewardsHistory.ts new file mode 100644 index 0000000000..c665be690c --- /dev/null +++ b/packages/staking/src/features/activity/helpers/getGroupedRewardsHistory.ts @@ -0,0 +1,9 @@ +import { AssetActivityListProps } from '@lace/core'; + +export const getGroupedRewardsActivities = (walletActivities: AssetActivityListProps[]) => + walletActivities + .map((group) => ({ + ...group, + items: group.items.filter((item) => item.type === 'rewards'), + })) + .filter((group) => group.items.length > 0); diff --git a/packages/staking/src/features/i18n/translations/en.ts b/packages/staking/src/features/i18n/translations/en.ts index 770069f7c3..4af359ddb3 100644 --- a/packages/staking/src/features/i18n/translations/en.ts +++ b/packages/staking/src/features/i18n/translations/en.ts @@ -1,6 +1,7 @@ import { Translations } from '../types'; export const en: Translations = { + 'activity.rewardsHistory.noStakingActivityYet': 'No staking activity yet.', 'activity.rewardsHistory.title': 'History', 'browsePools.stakePoolTableBrowser.addPool': 'Add pool', 'browsePools.stakePoolTableBrowser.disabledTooltip': 'Maximum number of pools selected', diff --git a/packages/staking/src/features/i18n/types.ts b/packages/staking/src/features/i18n/types.ts index 7b9e6a09b2..31fc5b4e40 100644 --- a/packages/staking/src/features/i18n/types.ts +++ b/packages/staking/src/features/i18n/types.ts @@ -18,6 +18,7 @@ type KeysStructure = { activity: { rewardsHistory: { title: ''; + noStakingActivityYet: ''; }; }; browsePools: { diff --git a/packages/staking/src/features/theme/colors.ts b/packages/staking/src/features/theme/colors.ts index 555f53a090..da605a277b 100644 --- a/packages/staking/src/features/theme/colors.ts +++ b/packages/staking/src/features/theme/colors.ts @@ -1,6 +1,7 @@ import { darkColorScheme, laceGradient, lightColorScheme } from '@lace/ui'; export const colorsContract = { + $activityNoActivityTextColor: '', $bannerBellIconColor: '', $bannerInfoIconColor: '', $delegationCardInfoLabelColor: '', @@ -29,6 +30,7 @@ export const colorsContract = { }; export const lightThemeColors: typeof colorsContract = { + $activityNoActivityTextColor: lightColorScheme.$primary_dark_grey, $bannerBellIconColor: lightColorScheme.$primary_accent_purple, $bannerInfoIconColor: lightColorScheme.$primary_accent_purple, $delegationCardInfoLabelColor: lightColorScheme.$primary_dark_grey, @@ -57,6 +59,7 @@ export const lightThemeColors: typeof colorsContract = { }; export const darkThemeColors: typeof colorsContract = { + $activityNoActivityTextColor: darkColorScheme.$primary_light_grey, $bannerBellIconColor: darkColorScheme.$primary_accent_purple, $bannerInfoIconColor: darkColorScheme.$primary_accent_purple, $delegationCardInfoLabelColor: darkColorScheme.$primary_light_grey, From fc496c239f274ca2ef63e0954051d9ba179745ef Mon Sep 17 00:00:00 2001 From: refi93 Date: Fri, 3 Nov 2023 17:13:58 +0100 Subject: [PATCH 4/4] fix(staking): align "no staking activity" component to center vertically --- packages/staking/src/features/activity/NoStakingActivity.tsx | 2 +- packages/staking/src/features/staking/StakingView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/staking/src/features/activity/NoStakingActivity.tsx b/packages/staking/src/features/activity/NoStakingActivity.tsx index 3e68ea8617..e2c928f3a9 100644 --- a/packages/staking/src/features/activity/NoStakingActivity.tsx +++ b/packages/staking/src/features/activity/NoStakingActivity.tsx @@ -7,7 +7,7 @@ import * as styles from './NoStakingActivity.css'; export const NoStakingActivity = () => { const { t } = useTranslation(); return ( - + {t('activity.rewardsHistory.noStakingActivityYet')} diff --git a/packages/staking/src/features/staking/StakingView.tsx b/packages/staking/src/features/staking/StakingView.tsx index 021f96a418..4abae0fd94 100644 --- a/packages/staking/src/features/staking/StakingView.tsx +++ b/packages/staking/src/features/staking/StakingView.tsx @@ -44,7 +44,7 @@ export const StakingView = () => { {(activePage) => ( - + {activePage === Page.overview && } {activePage === Page.browsePools && } {activePage === Page.activity && }