diff --git a/package.json b/package.json index dee3e98..62c467c 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@cosmjs/proto-signing": "^0.32.1", "@cosmjs/stargate": "^0.32.1", "@cosmjs/tendermint-rpc": "^0.32.1", - "@dydxprotocol/v4-abacus": "^1.3.2", + "@dydxprotocol/v4-abacus": "^1.3.4", "@dydxprotocol/v4-client-js": "^1.0.20", "@dydxprotocol/v4-localization": "^1.1.22", "@ethersproject/providers": "^5.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40c1b4f..8b2693f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,8 @@ dependencies: specifier: ^0.32.1 version: 0.32.1 '@dydxprotocol/v4-abacus': - specifier: ^1.3.2 - version: 1.3.2 + specifier: ^1.3.4 + version: 1.3.4 '@dydxprotocol/v4-client-js': specifier: ^1.0.20 version: 1.0.20 @@ -1102,8 +1102,8 @@ packages: resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==} dev: true - /@dydxprotocol/v4-abacus@1.3.2: - resolution: {integrity: sha512-zo0IHjGMlJRKOYDgNqNFQ9GtBJKJP4+Y9YY7V0X3Wt61ppKAYzodaYQhc9V/RYchcZTtS/xkicLug444YrvehQ==} + /@dydxprotocol/v4-abacus@1.3.4: + resolution: {integrity: sha512-6P61ZEMFqDit5G1+1yQSrDQWT51ap1jqF+ba/oaSg30CMUuhD2+1z6lDtUZbPw5+TjE4f7UyN/LYBTwylSiobA==} dev: false /@dydxprotocol/v4-client-js@1.0.20: @@ -14933,3 +14933,8 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: true + + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/src/components/Output.tsx b/src/components/Output.tsx index 8df09d2..ec55d8f 100644 --- a/src/components/Output.tsx +++ b/src/components/Output.tsx @@ -65,6 +65,9 @@ type ElementProps = { resolution?: number; stripRelativeWords?: boolean; }; + timeOptions?: { + useUTC?: boolean; + }; tag?: React.ReactNode; withParentheses?: boolean; locale?: string; @@ -89,6 +92,7 @@ export const Output = ({ relativeTimeFormatOptions = { format: 'singleCharacter', }, + timeOptions, tag, withParentheses, locale = navigator.language || 'en-US', @@ -166,16 +170,21 @@ export const Output = ({ if ((typeof value !== 'string' && typeof value !== 'number') || !value) return null; const date = new Date(value); const dateString = { - [OutputType.Date]: date.toLocaleString(selectedLocale, { dateStyle: 'medium' }), + [OutputType.Date]: date.toLocaleString(selectedLocale, { + dateStyle: 'medium', + timeZone: timeOptions?.useUTC ? 'UTC' : undefined, + }), [OutputType.DateTime]: date.toLocaleString(selectedLocale, { dateStyle: 'short', timeStyle: 'short', + timeZone: timeOptions?.useUTC ? 'UTC' : undefined, }), [OutputType.Time]: date.toLocaleString(selectedLocale, { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', + timeZone: timeOptions?.useUTC ? 'UTC' : undefined, }), }[type]; diff --git a/src/components/Table.tsx b/src/components/Table.tsx index ce2c89b..7c0d692 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -68,7 +68,7 @@ export type TableItem = { onSelect?: (key: TableRowData) => void; }; -type ColumnDef = { +export type ColumnDef = { columnKey: string; label: React.ReactNode; tag?: React.ReactNode; @@ -513,7 +513,7 @@ const TableColumnHeader = ({ export const ViewMoreRow = ({ colSpan, onClick }: { colSpan: number; onClick: () => void }) => { const stringGetter = useStringGetter(); return ( - + e.preventDefault()} @@ -523,7 +523,7 @@ export const ViewMoreRow = ({ colSpan, onClick }: { colSpan: number; onClick: () {stringGetter({ key: STRING_KEYS.VIEW_MORE })} - + ); }; @@ -673,6 +673,8 @@ Styled.TableWrapper = styled.div<{ --table-lastColumn-cell-align: end; // start | center | end | var(--table-cell-align) --tableCell-padding: 0 1rem; + --tableViewMore-borderColor: inherit; + // Rules flex: 1; @@ -984,3 +986,7 @@ Styled.ViewMoreButton = styled(Button)` margin-left: 0.5ch; } `; + +Styled.ViewMoreTr = styled(Styled.Tr)` + --border-color: var(--tableViewMore-borderColor); +`; diff --git a/src/constants/abacus.ts b/src/constants/abacus.ts index 1cdbfcc..2aed057 100644 --- a/src/constants/abacus.ts +++ b/src/constants/abacus.ts @@ -119,6 +119,13 @@ export const InputSelectionOption = Abacus.exchange.dydx.abacus.output.input.Sel // ------ Wallet ------ // export type Wallet = Abacus.exchange.dydx.abacus.output.Wallet; export type AccountBalance = Abacus.exchange.dydx.abacus.output.AccountBalance; +export type TradingRewards = Abacus.exchange.dydx.abacus.output.TradingRewards; +export type HistoricalTradingReward = Abacus.exchange.dydx.abacus.output.HistoricalTradingReward; +export const HistoricaTradingRewardsPeriod = + Abacus.exchange.dydx.abacus.state.manager.HistoricaTradingRewardsPeriod; +const historicalTradingRewardsPeriod = [...HistoricaTradingRewardsPeriod.values()] as const; +export type HistoricaTradingRewardsPeriods = (typeof historicalTradingRewardsPeriod)[number]; + export type Subaccount = Abacus.exchange.dydx.abacus.output.Subaccount; export type SubaccountPosition = Abacus.exchange.dydx.abacus.output.SubaccountPosition; export type SubaccountOrder = Abacus.exchange.dydx.abacus.output.SubaccountOrder; @@ -236,6 +243,15 @@ export const HISTORICAL_PNL_PERIODS: Record< [HistoricalPnlPeriod.Period90d.name]: HistoricalPnlPeriod.Period90d, }; +export const HISTORICAL_TRADING_REWARDS_PERIODS: Record< + KotlinIrEnumValues, + HistoricaTradingRewardsPeriods +> = { + [HistoricaTradingRewardsPeriod.MONTHLY.name]: HistoricaTradingRewardsPeriod.MONTHLY, + [HistoricaTradingRewardsPeriod.WEEKLY.name]: HistoricaTradingRewardsPeriod.WEEKLY, + [HistoricaTradingRewardsPeriod.DAILY.name]: HistoricaTradingRewardsPeriod.DAILY, +}; + export const ORDER_STATUS_STRINGS: Record, string> = { [AbacusOrderStatus.open.name]: STRING_KEYS.OPEN_STATUS, [AbacusOrderStatus.open.rawValue]: STRING_KEYS.OPEN_STATUS, diff --git a/src/lib/abacus/index.ts b/src/lib/abacus/index.ts index 199f834..84dfce8 100644 --- a/src/lib/abacus/index.ts +++ b/src/lib/abacus/index.ts @@ -9,6 +9,9 @@ import type { TransferInputFields, HistoricalPnlPeriods, ParsingError, + HistoricaTradingRewardsPeriods, + HistoricaTradingRewardsPeriod, + HistoricalTradingReward, } from '@/constants/abacus'; import { @@ -227,6 +230,12 @@ class AbacusStateManager { this.stateManager.historicalPnlPeriod = period; }; + setHistoricalTradingRewardPeriod = ( + period: (typeof HistoricaTradingRewardsPeriod)[keyof typeof HistoricaTradingRewardsPeriod] + ) => { + this.stateManager.historicalTradingRewardPeriod = period; + }; + switchNetwork = (network: DydxNetwork) => { this.stateManager.environmentId = network; @@ -278,6 +287,9 @@ class AbacusStateManager { getHistoricalPnlPeriod = (): Nullable => this.stateManager.historicalPnlPeriod; + getHistoricalTradingRewardPeriod = (): HistoricaTradingRewardsPeriods => + this.stateManager.historicalTradingRewardPeriod; + handleCandlesSubscription = ({ channelId, subscribe, diff --git a/src/lib/abacus/stateNotification.ts b/src/lib/abacus/stateNotification.ts index 216b7b7..b8b328c 100644 --- a/src/lib/abacus/stateNotification.ts +++ b/src/lib/abacus/stateNotification.ts @@ -29,6 +29,7 @@ import { setSubaccount, setTransfers, setWallet, + setTradingRewards, } from '@/state/account'; import { setApiState } from '@/state/app'; @@ -96,6 +97,12 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol { } } + if (changes.has(Changes.tradingRewards)) { + if (updatedState.account?.tradingRewards) { + dispatch(setTradingRewards(updatedState.account?.tradingRewards)); + } + } + if (changes.has(Changes.configs)) { dispatch(setConfigs(updatedState.configs)); } diff --git a/src/lib/testFlags.ts b/src/lib/testFlags.ts index 66c4b59..36f8a8b 100644 --- a/src/lib/testFlags.ts +++ b/src/lib/testFlags.ts @@ -26,6 +26,10 @@ class TestFlags { get addressOverride():string { return this.queryParams.address; } + + get showTradingRewards() { + return !!this.queryParams.tradingrewards; + } } export const testFlags = new TestFlags(); diff --git a/src/pages/rewards/RewardHistoryPanel.tsx b/src/pages/rewards/RewardHistoryPanel.tsx new file mode 100644 index 0000000..51fc70c --- /dev/null +++ b/src/pages/rewards/RewardHistoryPanel.tsx @@ -0,0 +1,101 @@ +import { useCallback, useState } from 'react'; +import styled, { AnyStyledComponent } from 'styled-components'; + +import breakpoints from '@/styles/breakpoints'; +import { layoutMixins } from '@/styles/layoutMixins'; +import { useStringGetter } from '@/hooks'; + +import { + HISTORICAL_TRADING_REWARDS_PERIODS, + HistoricaTradingRewardsPeriod, + HistoricaTradingRewardsPeriods, +} from '@/constants/abacus'; + +import { STRING_KEYS } from '@/constants/localization'; + +import { Panel } from '@/components/Panel'; +import { ToggleGroup } from '@/components/ToggleGroup'; +import { TradingRewardHistoryTable } from '@/views/tables/TradingRewardHistoryTable'; + +import abacusStateManager from '@/lib/abacus'; + +export const RewardHistoryPanel = () => { + const stringGetter = useStringGetter(); + + const [selectedPeriod, setSelectedPeriod] = useState( + abacusStateManager.getHistoricalTradingRewardPeriod() || HistoricaTradingRewardsPeriod.WEEKLY + ); + + const onSelectPeriod = useCallback( + (periodName: string) => { + const selectedPeriod = + HISTORICAL_TRADING_REWARDS_PERIODS[ + periodName as keyof typeof HISTORICAL_TRADING_REWARDS_PERIODS + ]; + setSelectedPeriod(selectedPeriod); + abacusStateManager.setHistoricalTradingRewardPeriod(selectedPeriod); + }, + [setSelectedPeriod, selectedPeriod] + ); + + return ( + + +

{stringGetter({ key: STRING_KEYS.REWARD_HISTORY })}

+ {stringGetter({ key: STRING_KEYS.REWARD_HISTORY_DESCRIPTION })} +
+ + + } + > + +
+ ); +}; + +const Styled: Record = {}; + +Styled.Header = styled.div` + ${layoutMixins.spacedRow} + + padding: 1rem 1rem 0; + margin-bottom: -0.5rem; + + @media ${breakpoints.notTablet} { + padding: 1.25rem 1.5rem 0; + } +`; + +Styled.Title = styled.div` + color: var(--color-text-0); + font: var(--font-small-book); + + h3 { + font: var(--font-medium-book); + color: var(--color-text-2); + } +`; + +Styled.Content = styled.div` + ${layoutMixins.flexColumn} + gap: 0.75rem; +`; diff --git a/src/pages/rewards/RewardsHelpPanel.tsx b/src/pages/rewards/RewardsHelpPanel.tsx index ac26d95..6696e37 100644 --- a/src/pages/rewards/RewardsHelpPanel.tsx +++ b/src/pages/rewards/RewardsHelpPanel.tsx @@ -29,20 +29,18 @@ export const RewardsHelpPanel = () => { > @@ -72,7 +70,7 @@ Styled.Header = styled.div` font: var(--font-small-book); @media ${breakpoints.notTablet} { - padding: 1.5rem 1.25rem; + padding: 1.5rem; } h3 { diff --git a/src/pages/rewards/RewardsPage.tsx b/src/pages/rewards/RewardsPage.tsx index 4329ca6..cd1b933 100644 --- a/src/pages/rewards/RewardsPage.tsx +++ b/src/pages/rewards/RewardsPage.tsx @@ -1,5 +1,4 @@ -import styled, { AnyStyledComponent } from 'styled-components'; -import { useDispatch } from 'react-redux'; +import styled, { AnyStyledComponent, css } from 'styled-components'; import { useNavigate } from 'react-router-dom'; import { STRING_KEYS } from '@/constants/localization'; @@ -11,18 +10,21 @@ import { breakpoints } from '@/styles'; import { layoutMixins } from '@/styles/layoutMixins'; import { BackButton } from '@/components/BackButton'; -import { Panel } from '@/components/Panel'; + +import { testFlags } from '@/lib/testFlags'; import { DYDXBalancePanel } from './DYDXBalancePanel'; import { LaunchIncentivesPanel } from './LaunchIncentivesPanel'; import { MigratePanel } from './MigratePanel'; import { RewardsHelpPanel } from './RewardsHelpPanel'; +import { TradingRewardsSummaryPanel } from './TradingRewardsSummaryPanel'; +import { RewardHistoryPanel } from './RewardHistoryPanel'; import { GovernancePanel } from './GovernancePanel'; import { StakingPanel } from './StakingPanel'; import { NewMarketsPanel } from './NewMarketsPanel'; + const RewardsPage = () => { - const dispatch = useDispatch(); const stringGetter = useStringGetter(); const { isTablet, isNotTablet } = useBreakpoints(); const navigate = useNavigate(); @@ -35,25 +37,35 @@ const RewardsPage = () => { {stringGetter({ key: STRING_KEYS.TRADING_REWARDS })} )} - {import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && } + + {import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && } - {isTablet ? ( - - ) : ( - - - - - )} - {isNotTablet && ( - - - - - - )} + {isTablet ? ( + + ) : ( + <> + + + + )} - + {testFlags.showTradingRewards && ( + + + {isTablet && } + + + )} + + {isNotTablet && ( + + + + + + + )} + ); }; @@ -64,7 +76,6 @@ const Styled: Record = {}; Styled.Page = styled.div` ${layoutMixins.contentContainerPage} - gap: 1.5rem; padding: 2rem; align-items: center; @@ -89,27 +100,73 @@ Styled.MobileHeader = styled.header` ${layoutMixins.stickyHeader} z-index: 2; padding: 1.25rem 0; - margin-bottom: -1.5rem; font: var(--font-large-medium); color: var(--color-text-2); background-color: var(--color-layer-2); `; -Styled.Panel = styled(Panel)` - height: fit-content; -`; +Styled.GridLayout = styled.div<{ showTradingRewards?: boolean }>` + --gap: 1.5rem; + display: grid; + grid-template-columns: 2fr 1fr; + gap: var(--gap); -Styled.PanelRow = styled.div` - ${layoutMixins.gridEqualColumns} - gap: 1.5rem; + > * { + gap: var(--gap); + } + + grid-template-areas: + 'migrate migrate' + 'incentives balance' + 'other other'; + + ${({ showTradingRewards }) => + showTradingRewards && + css` + grid-template-areas: + 'migrate migrate' + 'incentives balance' + 'rewards other'; + `} @media ${breakpoints.tablet} { - grid-auto-flow: row; + --gap: 1rem; grid-template-columns: 1fr; + grid-template-areas: + 'incentives' + 'rewards'; } `; -Styled.PanelRowIncentivesAndBalance = styled(Styled.PanelRow)` - grid-template-columns: 2fr 1fr; +Styled.MigratePanel = styled(MigratePanel)` + grid-area: migrate; +`; + +Styled.LaunchIncentivesPanel = styled(LaunchIncentivesPanel)` + grid-area: incentives; +`; + +Styled.DYDXBalancePanel = styled(DYDXBalancePanel)` + grid-area: balance; +`; + +Styled.TradingRewardsColumn = styled.div` + grid-area: rewards; + ${layoutMixins.flexColumn} +`; + +Styled.OtherColumn = styled.div` + grid-area: other; + ${layoutMixins.flexColumn} +`; + +Styled.RewardHistoryHeader = styled.div` + h3 { + font: var(--font-medium-book); + color: var(--color-text-2); + } + + padding: 1rem 1.5rem 0; + margin-bottom: -0.5rem; `; diff --git a/src/pages/rewards/TradingRewardsSummaryPanel.tsx b/src/pages/rewards/TradingRewardsSummaryPanel.tsx new file mode 100644 index 0000000..17b9f27 --- /dev/null +++ b/src/pages/rewards/TradingRewardsSummaryPanel.tsx @@ -0,0 +1,136 @@ +import styled, { AnyStyledComponent } from 'styled-components'; +import { shallowEqual, useSelector } from 'react-redux'; + +import { STRING_KEYS } from '@/constants/localization'; +import { layoutMixins } from '@/styles/layoutMixins'; +import { useStringGetter, useTokenConfigs } from '@/hooks'; + +import { AssetIcon } from '@/components/AssetIcon'; +import { Details } from '@/components/Details'; +import { Output, OutputType } from '@/components/Output'; +import { Panel } from '@/components/Panel'; + +import { getHistoricalTradingRewardsForPeriod } from '@/state/accountSelectors'; + +export const TradingRewardsSummaryPanel = () => { + const stringGetter = useStringGetter(); + const { chainTokenLabel } = useTokenConfigs(); + + const currentWeekTradingRewards = useSelector( + getHistoricalTradingRewardsForPeriod('WEEKLY'), + shallowEqual + ); + const currentWeekTradingReward = currentWeekTradingRewards?.firstOrNull(); + + return ( + currentWeekTradingReward && ( + + {stringGetter({ key: STRING_KEYS.TRADING_REWARDS_SUMMARY })} + + } + > + + +

{stringGetter({ key: STRING_KEYS.THIS_WEEK })}

+ + ), + value: ( + + } + type={OutputType.Asset} + value={currentWeekTradingReward.amount} + /> + + + → + + + + ), + }, + // TODO(@aforaleka): add all-time when supported + ]} + /> +
+
+ ) + ); +}; + +const Styled: Record = {}; + +Styled.Header = styled.div` + padding: var(--panel-paddingY) var(--panel-paddingX) 0; + font: var(--font-medium-book); + color: var(--color-text-2); +`; + +Styled.Content = styled.div` + ${layoutMixins.flexColumn} + gap: 0.75rem; +`; + +Styled.TradingRewardsDetails = styled(Details)` + --details-item-backgroundColor: var(--color-layer-6); + + grid-template-columns: 1fr; // TODO(@aforaleka): change to 1fr 1fr when all-time is supported + gap: 1rem; + + > div { + gap: 0.5rem; + padding: 1rem; + border-radius: 0.75em; + background-color: var(--color-layer-5); + } + + dt { + width: 100%; + } + + output { + color: var(--color-text-2); + font: var(--font-large-book); + } +`; + +Styled.Label = styled.div` + ${layoutMixins.spacedRow} + + font: var(--font-base-book); + color: var(--color-text-1); +`; + +Styled.TimePeriod = styled.div` + ${layoutMixins.inlineRow} + + &, output { + color: var(--color-text-0); + font: var(--font-small-book); + } +`; + +Styled.Column = styled.div` + ${layoutMixins.flexColumn} + gap: 0.33rem; +`; + +Styled.AssetIcon = styled(AssetIcon)` + margin-left: 0.5ch; +`; diff --git a/src/state/account.ts b/src/state/account.ts index aae7b2d..57d7a54 100644 --- a/src/state/account.ts +++ b/src/state/account.ts @@ -13,6 +13,7 @@ import type { HistoricalPnlPeriods, SubAccountHistoricalPNLs, UsageRestriction, + TradingRewards, } from '@/constants/abacus'; import { OnboardingGuard, OnboardingState } from '@/constants/account'; @@ -24,6 +25,7 @@ import { getLocalStorage } from '@/lib/localStorage'; export type AccountState = { balances?: Record; stakingBalances?: Record; + tradingRewards?: TradingRewards; wallet?: Nullable; walletType?: WalletType; @@ -179,6 +181,9 @@ export const accountSlice = createSlice({ setStakingBalances: (state, action: PayloadAction>) => { state.stakingBalances = action.payload; }, + setTradingRewards: (state, action: PayloadAction) => { + state.tradingRewards = action.payload; + }, addUncommittedOrderClientId: (state, action: PayloadAction) => { state.uncommittedOrderClientIds.push(action.payload); }, @@ -206,6 +211,7 @@ export const { viewedOrders, setBalances, setStakingBalances, + setTradingRewards, addUncommittedOrderClientId, removeUncommittedOrderClientId, } = accountSlice.actions; diff --git a/src/state/accountSelectors.ts b/src/state/accountSelectors.ts index 809a027..8ad2340 100644 --- a/src/state/accountSelectors.ts +++ b/src/state/accountSelectors.ts @@ -9,6 +9,7 @@ import { AbacusOrderStatus, AbacusPositionSide, ORDER_SIDES, + HistoricalTradingReward, } from '@/constants/abacus'; import { OnboardingState } from '@/constants/account'; @@ -349,6 +350,25 @@ export const getBalances = (state: RootState) => state.account?.balances; * */ export const getStakingBalances = (state: RootState) => state.account?.stakingBalances; +/** + * @returns account all time trading rewards + */ +export const getTotalTradingRewards = (state: RootState) => state.account?.tradingRewards?.total; + +/** + * @returns account trading rewards aggregated by period + */ +export const getHistoricalTradingRewards = (state: RootState) => + state.account?.tradingRewards?.historical; + +/** + * @returns account historical trading rewards for the specified perid + */ +export const getHistoricalTradingRewardsForPeriod = (period: string) => + createSelector([getHistoricalTradingRewards], (historicalTradingRewards: any) => + historicalTradingRewards?.get(period) + ); + /** * @returns UsageRestriction of the current session */ diff --git a/src/views/tables/TradingRewardHistoryTable.tsx b/src/views/tables/TradingRewardHistoryTable.tsx new file mode 100644 index 0000000..9a58d6d --- /dev/null +++ b/src/views/tables/TradingRewardHistoryTable.tsx @@ -0,0 +1,158 @@ +import styled, { type AnyStyledComponent } from 'styled-components'; +import { shallowEqual, useSelector } from 'react-redux'; + +import { HistoricaTradingRewardsPeriods } from '@/constants/abacus'; +import { STRING_KEYS, StringGetterFunction } from '@/constants/localization'; +import { useStringGetter, useTokenConfigs } from '@/hooks'; +import { layoutMixins } from '@/styles/layoutMixins'; + +import { AssetIcon } from '@/components/AssetIcon'; +import { Output, OutputType } from '@/components/Output'; +import { Table, TableCell, type ColumnDef } from '@/components/Table'; + +import { getHistoricalTradingRewardsForPeriod } from '@/state/accountSelectors'; + +export enum TradingRewardHistoryTableColumnKey { + Event = 'Event', + Earned = 'Earned', +} + +const getTradingRewardHistoryTableColumnDef = ({ + key, + chainTokenLabel, + stringGetter, +}: { + key: TradingRewardHistoryTableColumnKey; + chainTokenLabel: string; + stringGetter: StringGetterFunction; +}): ColumnDef => ({ + ...( + { + [TradingRewardHistoryTableColumnKey.Event]: { + columnKey: TradingRewardHistoryTableColumnKey.Event, + getCellValue: (row) => row.startedAtInMilliseconds, + label: stringGetter({ key: STRING_KEYS.EVENT }), + renderCell: ({ startedAtInMilliseconds, endedAtInMilliseconds }) => ( + + {stringGetter({ key: STRING_KEYS.REWARDED })} + + {stringGetter({ + key: STRING_KEYS.FOR_TRADING, + params: { + PERIOD: ( + <> + + → + + + ), + }, + })} + + + ), + }, + [TradingRewardHistoryTableColumnKey.Earned]: { + columnKey: TradingRewardHistoryTableColumnKey.Earned, + getCellValue: (row) => row.amount, + label: stringGetter({ key: STRING_KEYS.EARNED }), + renderCell: ({ amount }) => ( + } + /> + ), + }, + } as Record> + )[key], +}); + +type ElementProps = { + columnKeys?: TradingRewardHistoryTableColumnKey[]; + period: HistoricaTradingRewardsPeriods; +}; + +type StyleProps = { + withOuterBorder?: boolean; + withInnerBorders?: boolean; +}; + +export const TradingRewardHistoryTable = ({ + period, + columnKeys = Object.values(TradingRewardHistoryTableColumnKey), + withOuterBorder, + withInnerBorders = true, +}: ElementProps & StyleProps) => { + const stringGetter = useStringGetter(); + const { chainTokenLabel } = useTokenConfigs(); + + const periodTradingRewards = useSelector( + getHistoricalTradingRewardsForPeriod(period.name), + shallowEqual + ); + + return ( + row.startedAtInMilliseconds} + columns={columnKeys.map((key: TradingRewardHistoryTableColumnKey) => + getTradingRewardHistoryTableColumnDef({ + key, + chainTokenLabel, + stringGetter, + }) + )} + selectionBehavior="replace" + withOuterBorder={withOuterBorder} + withInnerBorders={withInnerBorders} + initialNumRowsToShow={5} + withScrollSnapColumns + withScrollSnapRows + /> + ); +}; + +const Styled: Record = {}; + +Styled.Table = styled(Table)` + --tableCell-padding: 0.5rem 0; + --tableHeader-backgroundColor: var(--color-layer-3); + --tableRow-backgroundColor: var(--color-layer-3); + --tableViewMore-borderColor: var(--color-layer-3); + + tbody { + font: var(--font-medium-book); + } +`; + +Styled.Rewarded = styled.span` + color: var(--color-text-2); +`; + +Styled.TimePeriod = styled.div` + ${layoutMixins.inlineRow} + + && { + color: var(--color-text-0); + font: var(--font-base-book); + } + + output { + color: var(--color-text-1); + font: var(--font-base-book); + } +`; + +Styled.AssetIcon = styled(AssetIcon)` + margin-left: 0.5ch; +`;