diff --git a/__tests__/components/Account/AccountDetails.test.tsx b/__tests__/components/Account/AccountDetails.test.tsx index 90b1de80..99b2f944 100644 --- a/__tests__/components/Account/AccountDetails.test.tsx +++ b/__tests__/components/Account/AccountDetails.test.tsx @@ -14,15 +14,28 @@ jest.mock('hooks/useHealthComputer', () => jest.mock('components/Account/AccountBalancesTable', () => jest.fn(() => null)) const mockedUseCurrentAccount = useCurrentAccount as jest.Mock -const mockedAccount = { id: '1', deposits: [], lends: [], debts: [], vaults: [] } +const mockedAccounts = [ + { id: '1', deposits: [], lends: [], debts: [], vaults: [] }, + { id: '2', deposits: [], lends: [], debts: [], vaults: [] }, +] jest.mock('hooks/useAccountId', () => jest.fn(() => '1')) -jest.mock('hooks/useAccounts', () => jest.fn(() => [mockedAccount])) +jest.mock('hooks/useAccounts', () => + jest.fn(() => ({ + data: mockedAccounts, + })), +) +jest.mock('hooks/useAccountIds', () => + jest.fn(() => ({ + data: ['1', '2'], + })), +) +jest.mock('hooks/useCurrentAccount', () => jest.fn(() => mockedAccounts[0])) describe('', () => { beforeAll(() => { useStore.setState({ address: 'walletAddress', - accounts: [mockedAccount], + accounts: mockedAccounts, }) }) @@ -31,7 +44,7 @@ describe('', () => { }) it('renders account details WHEN account is selected', () => { - mockedUseCurrentAccount.mockReturnValue(mockedAccount) + mockedUseCurrentAccount.mockReturnValue(mockedAccounts) render() const container = screen.queryByTestId('account-details') diff --git a/next.config.js b/next.config.js index f293ba9f..3915291a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - productionBrowserSourceMaps: true, reactStrictMode: true, images: { domains: [ @@ -26,6 +25,31 @@ const nextConfig = { }, ] }, + async headers() { + return [ + { + source: '/(.*)?', + headers: [ + { + key: 'Referrer-Policy', + value: 'origin-when-cross-origin', + }, + { + key: 'Pragma', + value: 'no-cache', + }, + { + key: 'Expires', + value: new Date().toString(), + }, + { + key: 'X-Content-Type-Options', + value: 'nosniff', + }, + ], + }, + ] + }, async rewrites() { return [ { diff --git a/src/api/wallets/getAccountIds.ts b/src/api/wallets/getAccountIds.ts index 55cfbe99..de876130 100644 --- a/src/api/wallets/getAccountIds.ts +++ b/src/api/wallets/getAccountIds.ts @@ -5,8 +5,8 @@ export default async function getAccountIds( address?: string, previousResults?: string[], ): Promise { + if (!address) return [] try { - if (!address) return [] const accountNftQueryClient = await getAccountNftQueryClient() const lastItem = previousResults && previousResults.at(-1) diff --git a/src/components/Account/AccountDetails/index.tsx b/src/components/Account/AccountDetails/index.tsx index ee24f77a..9d6ffe57 100644 --- a/src/components/Account/AccountDetails/index.tsx +++ b/src/components/Account/AccountDetails/index.tsx @@ -17,6 +17,7 @@ import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { REDUCE_MOTION_KEY } from 'constants/localStore' import { ORACLE_DENOM } from 'constants/oracle' import useAccountId from 'hooks/useAccountId' +import useAccountIds from 'hooks/useAccountIds' import useAccounts from 'hooks/useAccounts' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' import useCurrentAccount from 'hooks/useCurrentAccount' @@ -34,12 +35,15 @@ import { export default function AccountDetailsController() { const address = useStore((s) => s.address) - const { isLoading } = useAccounts(address) + const { data: accounts, isLoading } = useAccounts(address) + const { data: accountIds } = useAccountIds(address, false) const accountId = useAccountId() const account = useCurrentAccount() - const focusComponent = useStore((s) => s.focusComponent) - if (!address || focusComponent) return null + const focusComponent = useStore((s) => s.focusComponent) + const isOwnAccount = accountId && accountIds?.includes(accountId) + + if (!address || focusComponent || !isOwnAccount) return null if ((isLoading && accountId && !focusComponent) || !account) return diff --git a/src/components/Account/AccountList/index.tsx b/src/components/Account/AccountList/index.tsx index 29f96b49..c600ddd2 100644 --- a/src/components/Account/AccountList/index.tsx +++ b/src/components/Account/AccountList/index.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames' import { useEffect } from 'react' -import { useLocation, useNavigate, useParams } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' import AccountStats from 'components/Account/AccountList/AccountStats' import Card from 'components/Card' @@ -26,7 +26,7 @@ export default function AccountList(props: Props) { const navigate = useNavigate() const { pathname } = useLocation() const currentAccountId = useAccountId() - const { address } = useParams() + const address = useStore((s) => s.address) const { data: accountIds } = useAccountIds(address) useEffect(() => { @@ -37,7 +37,7 @@ export default function AccountList(props: Props) { } }, [currentAccountId]) - if (!accountIds?.length) return null + if (!accountIds || !accountIds.length) return null return (
diff --git a/src/components/Account/AccountMenuContent.tsx b/src/components/Account/AccountMenuContent.tsx index 324101c1..e1a939c9 100644 --- a/src/components/Account/AccountMenuContent.tsx +++ b/src/components/Account/AccountMenuContent.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames' import { useCallback } from 'react' -import { useLocation, useNavigate, useParams } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' import AccountCreateFirst from 'components/Account/AccountCreateFirst' import AccountFund from 'components/Account/AccountFund/AccountFundFullPage' @@ -30,7 +30,7 @@ const ACCOUNT_MENU_BUTTON_ID = 'account-menu-button' export default function AccountMenuContent() { const navigate = useNavigate() const { pathname } = useLocation() - const { address } = useParams() + const address = useStore((s) => s.address) const { data: accountIds } = useAccountIds(address) const accountId = useAccountId() @@ -42,8 +42,9 @@ export default function AccountMenuContent() { const [lendAssets] = useLocalStorage(LEND_ASSETS_KEY, DEFAULT_SETTINGS.lendAssets) const { enableAutoLendAccountId } = useAutoLend() - const hasCreditAccounts = !!accountIds.length - const isAccountSelected = isNumber(accountId) + const hasCreditAccounts = !!accountIds?.length + const isAccountSelected = + hasCreditAccounts && accountId && isNumber(accountId) && accountIds.includes(accountId) const checkHasFunds = useCallback(() => { return ( diff --git a/src/components/Button/ActionButton.tsx b/src/components/Button/ActionButton.tsx index d1ce6e2a..4b240d7b 100644 --- a/src/components/Button/ActionButton.tsx +++ b/src/components/Button/ActionButton.tsx @@ -23,7 +23,7 @@ export default function ActionButton(props: ButtonProps) { if (!address) return - if (accountIds.length === 0) { + if (accountIds && accountIds.length === 0) { return (
diff --git a/src/components/Portfolio/PortfolioIntro.tsx b/src/components/Portfolio/PortfolioIntro.tsx index 7ccac9d1..e359527a 100644 --- a/src/components/Portfolio/PortfolioIntro.tsx +++ b/src/components/Portfolio/PortfolioIntro.tsx @@ -4,17 +4,18 @@ import Intro from 'components/Intro' import useStore from 'store' export default function PortfolioIntro() { - const { address } = useParams() - const walletAddress = useStore((s) => s.address) + const { address: urlAddress } = useParams() + const address = useStore((s) => s.address) + const isCurrentWalllet = !urlAddress || urlAddress === address return ( This is the Portfolio of the address{' '} - {address}. You can see all Credit Accounts of this - address, but you can't interact with them. + {urlAddress}. You can see all Credit Accounts of + this address, but you can't interact with them. ) : ( <> diff --git a/src/components/Routes.tsx b/src/components/Routes.tsx index e4e0b096..df26dd94 100644 --- a/src/components/Routes.tsx +++ b/src/components/Routes.tsx @@ -3,6 +3,8 @@ import { Navigate, Outlet, Route, Routes as RoutesWrapper } from 'react-router-d import Layout from 'pages/_layout' import BorrowPage from 'pages/BorrowPage' import FarmPage from 'pages/FarmPage' +import HLSFarmPage from 'pages/HLSFarmPage' +import HLSStakingPage from 'pages/HLSStakingPage' import LendPage from 'pages/LendPage' import MobilePage from 'pages/MobilePage' import PortfolioAccountPage from 'pages/PortfolioAccountPage' @@ -25,6 +27,8 @@ export default function Routes() { } /> } /> } /> + } /> + } /> } /> } /> @@ -35,6 +39,8 @@ export default function Routes() { } /> + } /> + } /> } /> } /> diff --git a/src/components/ShareBar.tsx b/src/components/ShareBar.tsx new file mode 100644 index 00000000..9cea8d2e --- /dev/null +++ b/src/components/ShareBar.tsx @@ -0,0 +1,55 @@ +import classNames from 'classnames' +import { useLocation, useParams } from 'react-router-dom' +import useClipboard from 'react-use-clipboard' + +import Button from 'components/Button' +import { Chain, Check, Twitter } from 'components/Icons' +import Text from 'components/Text' +import { Tooltip } from 'components/Tooltip' +import ConditionalWrapper from 'hocs/ConditionalWrapper' +import { DocURL } from 'types/enums/docURL' + +interface Props { + text: string +} + +export default function ShareBar(props: Props) { + const { address } = useParams() + const { pathname } = useLocation() + const currentUrl = `https://${location.host}${pathname}` + const [isCopied, setCopied] = useClipboard(currentUrl, { + successDuration: 1000 * 5, + }) + + if (!window || !address) return null + return ( +
+ ( + Link copied!}> + {children} + + )} + > +
+ ) +} diff --git a/src/components/Trade/TradeChart/OsmosisTheGraphDataFeed.ts b/src/components/Trade/TradeChart/OsmosisTheGraphDataFeed.ts index 0fe4ac96..384c9d13 100644 --- a/src/components/Trade/TradeChart/OsmosisTheGraphDataFeed.ts +++ b/src/components/Trade/TradeChart/OsmosisTheGraphDataFeed.ts @@ -224,7 +224,8 @@ export class OsmosisTheGraphDataFeed implements IDatafeedChartApi { } const filler = Array.from({ length: this.batchSize - bars.length }).map((_, index) => ({ - time: (bars[0]?.time || new Date().getTime()) - index * this.minutesPerInterval[resolution], + time: + (bars[0]?.time || new Date().getTime()) - index * this.minutesPerInterval[resolution], close: 0, open: 0, high: 0, diff --git a/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx b/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx index 034e5323..b1155418 100644 --- a/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx +++ b/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx @@ -1,5 +1,5 @@ import { Suspense, useEffect, useMemo } from 'react' -import { useLocation, useNavigate } from 'react-router-dom' +import { useLocation, useNavigate, useParams } from 'react-router-dom' import AccountCreateFirst from 'components/Account/AccountCreateFirst' import { CircularProgress } from 'components/CircularProgress' @@ -27,6 +27,7 @@ function FetchLoading() { function Content() { const address = useStore((s) => s.address) + const { address: urlAddress } = useParams() const navigate = useNavigate() const { pathname } = useLocation() const { data: accountIds, isLoading: isLoadingAccounts } = useAccountIds(address || '') @@ -39,18 +40,26 @@ function Content() { ) useEffect(() => { + const page = getPage(pathname) + if (page === 'portfolio' && urlAddress && urlAddress !== address) { + navigate(getRoute(page, urlAddress as string)) + useStore.setState({ balances: walletBalances, focusComponent: null }) + return + } + if ( + accountIds && accountIds.length !== 0 && BN(baseBalance).isGreaterThanOrEqualTo(defaultFee.amount[0].amount) ) { - navigate(getRoute(getPage(pathname), address, accountIds[0])) + navigate(getRoute(page, address, accountIds[0])) useStore.setState({ balances: walletBalances, focusComponent: null }) } - }, [accountIds, baseBalance, navigate, pathname, address, walletBalances]) + }, [accountIds, baseBalance, navigate, pathname, address, walletBalances, urlAddress]) if (isLoadingAccounts || isLoadingBalances) return if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) return - if (accountIds.length === 0) return + if (accountIds && accountIds.length === 0) return return null } diff --git a/src/constants/pages.ts b/src/constants/pages.ts new file mode 100644 index 00000000..a649f1fa --- /dev/null +++ b/src/constants/pages.ts @@ -0,0 +1,9 @@ +export const EARN_TABS: Tab[] = [ + { page: 'lend', name: 'Lend' }, + { page: 'farm', name: 'Farm' }, +] + +export const HLS_TABS: Tab[] = [ + { page: 'hls-farm', name: 'Farm' }, + { page: 'hls-staking', name: 'Staking' }, +] diff --git a/src/hooks/useAccountIds.tsx b/src/hooks/useAccountIds.tsx index fffe385c..da633e92 100644 --- a/src/hooks/useAccountIds.tsx +++ b/src/hooks/useAccountIds.tsx @@ -2,9 +2,9 @@ import useSWR from 'swr' import getAccountIds from 'api/wallets/getAccountIds' -export default function useAccountIds(address?: string) { +export default function useAccountIds(address?: string, suspense = true) { return useSWR(`wallets/${address}/account-ids`, () => getAccountIds(address), { - suspense: true, + suspense: suspense, fallback: [] as string[], revalidateOnFocus: false, }) diff --git a/src/pages/FarmPage.tsx b/src/pages/FarmPage.tsx index fb248757..0eeccf7d 100644 --- a/src/pages/FarmPage.tsx +++ b/src/pages/FarmPage.tsx @@ -2,12 +2,13 @@ import FarmIntro from 'components/Earn/Farm/FarmIntro' import { AvailableVaults, DepositedVaults } from 'components/Earn/Farm/Vaults' import Tab from 'components/Earn/Tab' import MigrationBanner from 'components/MigrationBanner' +import { EARN_TABS } from 'constants/pages' export default function FarmPage() { return (
- + diff --git a/src/pages/HLSFarmPage.tsx b/src/pages/HLSFarmPage.tsx new file mode 100644 index 00000000..d386ee30 --- /dev/null +++ b/src/pages/HLSFarmPage.tsx @@ -0,0 +1,12 @@ +import Tab from 'components/Earn/Tab' +import MigrationBanner from 'components/MigrationBanner' +import { HLS_TABS } from 'constants/pages' + +export default function HLSFarmPage() { + return ( +
+ + +
+ ) +} diff --git a/src/pages/HLSStakingPage.tsx b/src/pages/HLSStakingPage.tsx new file mode 100644 index 00000000..1fc8e2d2 --- /dev/null +++ b/src/pages/HLSStakingPage.tsx @@ -0,0 +1,12 @@ +import Tab from 'components/Earn/Tab' +import MigrationBanner from 'components/MigrationBanner' +import { HLS_TABS } from 'constants/pages' + +export default function HLSStakingPage() { + return ( +
+ + +
+ ) +} diff --git a/src/pages/LendPage.tsx b/src/pages/LendPage.tsx index c27b76e3..0cce99ee 100644 --- a/src/pages/LendPage.tsx +++ b/src/pages/LendPage.tsx @@ -1,7 +1,8 @@ -import LendIntro from 'components/Earn/Lend/LendIntro' import LendingMarketsTable from 'components/Earn/Lend/LendingMarketsTable' +import LendIntro from 'components/Earn/Lend/LendIntro' import Tab from 'components/Earn/Tab' import MigrationBanner from 'components/MigrationBanner' +import { EARN_TABS } from 'constants/pages' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' export default function LendPage() { @@ -9,7 +10,7 @@ export default function LendPage() { return (
- + diff --git a/src/pages/PortfolioAccountPage.tsx b/src/pages/PortfolioAccountPage.tsx index fa63750a..84a9b835 100644 --- a/src/pages/PortfolioAccountPage.tsx +++ b/src/pages/PortfolioAccountPage.tsx @@ -4,6 +4,7 @@ import MigrationBanner from 'components/MigrationBanner' import Balances from 'components/Portfolio/Account/Balances' import BreadCrumbs from 'components/Portfolio/Account/BreadCrumbs' import Summary from 'components/Portfolio/Account/Summary' +import ShareBar from 'components/ShareBar' import useAccountId from 'hooks/useAccountId' import { getRoute } from 'utils/route' @@ -23,6 +24,7 @@ export default function PortfolioAccountPage() { +
) } diff --git a/src/pages/PortfolioPage.tsx b/src/pages/PortfolioPage.tsx index 9c676797..e39a71db 100644 --- a/src/pages/PortfolioPage.tsx +++ b/src/pages/PortfolioPage.tsx @@ -2,6 +2,7 @@ import MigrationBanner from 'components/MigrationBanner' import AccountOverview from 'components/Portfolio/Overview' import PortfolioSummary from 'components/Portfolio/Overview/Summary' import PortfolioIntro from 'components/Portfolio/PortfolioIntro' +import ShareBar from 'components/ShareBar' export default function PortfolioPage() { return ( @@ -10,6 +11,7 @@ export default function PortfolioPage() { +
) } diff --git a/src/types/enums/docURL.ts b/src/types/enums/docURL.ts index 7babde0c..aaeeec0d 100644 --- a/src/types/enums/docURL.ts +++ b/src/types/enums/docURL.ts @@ -3,6 +3,7 @@ export enum DocURL { BORROW_LENDING_URL = 'https://docs.marsprotocol.io/docs/learn/mars-v2/borrow', COOKIE_POLICY_URL = 'https://docs.marsprotocol.io/docs/overview/legal/cookie-policy', DOCS_URL = 'https://docs.marsprotocol.io/', + FARM_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/tutorials/farming/farming-intro', MANAGE_ACCOUNT_URL = 'https://docs.marsprotocol.io/docs/learn/tutorials/credit-accounts/credit-accounts-intro', ROVER_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/mars-v2/credit-accounts', PRIVACY_POLICY_URL = 'https://docs.marsprotocol.io/docs/overview/legal/privacy-policy', @@ -10,4 +11,5 @@ export enum DocURL { TRADING_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/tutorials/trading/trading-intro', V1_URL = 'https://v1.marsprotocol.io', WALLET_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/tutorials/basics/connecting-your-wallet', + X_SHARE_URL = 'https://x.com/intent/tweet', } diff --git a/src/types/interfaces/components/Tab.d.ts b/src/types/interfaces/components/Tab.d.ts new file mode 100644 index 00000000..aff1c9c5 --- /dev/null +++ b/src/types/interfaces/components/Tab.d.ts @@ -0,0 +1,4 @@ +interface Tab { + page: Page + name: string +} diff --git a/src/types/interfaces/route.d.ts b/src/types/interfaces/route.d.ts index 48101e31..dcd3b9e7 100644 --- a/src/types/interfaces/route.d.ts +++ b/src/types/interfaces/route.d.ts @@ -1 +1,9 @@ -type Page = 'trade' | 'borrow' | 'farm' | 'lend' | 'portfolio' | 'portfolio/{accountId}' +type Page = + | 'trade' + | 'borrow' + | 'farm' + | 'lend' + | 'portfolio' + | 'portfolio/{accountId}' + | 'hls-farm' + | 'hls-staking' diff --git a/src/utils/constants.ts b/src/utils/constants.ts index f4e8a264..55a7b2b4 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -22,3 +22,5 @@ export const DEFAULT_PORTFOLIO_STATS = [ { title: null, sub: 'APR' }, { title: null, sub: 'Account Leverage' }, ] + +export const ENABLE_HLS = false