Skip to content

Commit

Permalink
Portfolio improvements (#3924)
Browse files Browse the repository at this point in the history
* Closes FE-1188

* Closes FE-1009

* lowercase open orders

* hide nav bar title

* optimize rendering

* SSR swap tool

* clean up limit order ff & bg

* remove old swap tool code

* add knip JSON

* fix translations test

* coderabbit review
  • Loading branch information
jonator authored Oct 31, 2024
1 parent 780e00a commit 414bf41
Show file tree
Hide file tree
Showing 42 changed files with 757 additions and 2,159 deletions.
2 changes: 1 addition & 1 deletion packages/tx/src/__tests__/gas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ApiClientError } from "@osmosis-labs/utils";
import type { Any } from "cosmjs-types/google/protobuf/any";

import {
defaultBaseFeeMultiplier,
defaultBaseFeeMultiplier,
getDefaultGasPrice,
getGasFeeAmount,
getGasPriceByFeeDenom,
Expand Down
70 changes: 30 additions & 40 deletions packages/web/components/complex/portfolio/allocation.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Dec, PricePretty } from "@keplr-wallet/unit";
import { Dec } from "@keplr-wallet/unit";
import { GetAllocationResponse } from "@osmosis-labs/server";
import classNames from "classnames";
import { FunctionComponent, useEffect, useState } from "react";
import { FunctionComponent, useEffect, useMemo, useState } from "react";

import { Icon } from "~/components/assets";
import { AllocationTabs } from "~/components/complex/portfolio/allocation-tabs";
import { getIsDust } from "~/components/complex/portfolio/portfolio-dust";
import { AllocationOptions } from "~/components/complex/portfolio/types";
import { EventName } from "~/config";
import {
Breakpoint,
MultiLanguageT,
useAmplitudeAnalytics,
useHideDustUserSetting,
useTranslation,
useWindowSize,
} from "~/hooks";
Expand Down Expand Up @@ -56,17 +56,9 @@ const getTranslation = (key: string, t: MultiLanguageT): string => {
return translationMap[key] || key;
};

const shouldShowItemInSelectedList = (
hideDust: boolean,
fiatValue: PricePretty
) => {
return !hideDust || !getIsDust(fiatValue);
};

export const Allocation: FunctionComponent<{
allocation?: GetAllocationResponse;
hideDust: boolean;
}> = ({ allocation, hideDust }) => {
}> = ({ allocation }) => {
const { logEvent } = useAmplitudeAnalytics();
const { width } = useWindowSize();
const [selectedOption, setSelectedOption] =
Expand All @@ -80,9 +72,17 @@ export const Allocation: FunctionComponent<{
}
}, [width]);

if (!allocation) return null;
const selectedList = useMemo(
() => allocation?.[selectedOption] ?? [],
[allocation, selectedOption]
);

const selectedList = allocation[selectedOption];
const dustFilteredList = useHideDustUserSetting(
selectedList,
(item) => item.fiatValue
);

if (!allocation) return null;

return (
<div className="flex w-full flex-col py-3">
Expand Down Expand Up @@ -115,40 +115,30 @@ export const Allocation: FunctionComponent<{
/>
</div>
<div className="my-[8px] flex h-4 w-full gap-1">
{selectedList
.filter(({ fiatValue }) =>
shouldShowItemInSelectedList(hideDust, fiatValue)
)
.map(({ key, percentage }, index) => {
const colorClass =
COLORS[selectedOption][index % COLORS[selectedOption].length];
{dustFilteredList.map(({ key, percentage }, index) => {
const colorClass =
COLORS[selectedOption][index % COLORS[selectedOption].length];

const isNegligiblePercent = percentage
.toDec()
.lt(new Dec(0.01));
const isNegligiblePercent = percentage.toDec().lt(new Dec(0.01));

const width = isNegligiblePercent
? "0.1%"
: percentage.toString();
const width = isNegligiblePercent
? "0.1%"
: percentage.toString();

return (
<div
key={key}
className={classNames("h-full rounded-[4px]", colorClass)}
style={{ width }}
/>
);
})}
return (
<div
key={key}
className={classNames("h-full rounded-[4px]", colorClass)}
style={{ width }}
/>
);
})}
</div>
<div className="flex flex-col space-y-3">
{selectedList.map(({ key, percentage, fiatValue }, index) => {
{dustFilteredList.map(({ key, percentage, fiatValue }, index) => {
const colorClass =
COLORS[selectedOption][index % COLORS[selectedOption].length];

const isDust = getIsDust(fiatValue);

if (hideDust && isDust) return null;

return (
<div key={key} className="body2 flex w-full justify-between">
<div className="flex items-center space-x-1">
Expand Down
27 changes: 24 additions & 3 deletions packages/web/components/complex/portfolio/cypher-card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Link from "next/link";
import { useLocalStorage } from "react-use";

import { Icon } from "~/components/assets";
import { useTranslation } from "~/hooks";
Expand All @@ -7,9 +8,18 @@ export const CYPHER_CARD_URL = "https://pay.osmosis.zone?ref=osmosis";

export const CypherCard = () => {
const { t } = useTranslation();
const [isClosed, setIsClosed] = useLocalStorage("cypher-card-closed", false);

if (isClosed) return null;

return (
<Link href={CYPHER_CARD_URL} target="_blank" rel="noopener noreferrer">
<div className="flex h-[4.5rem] w-full flex-1 items-center gap-4 rounded-2xl bg-osmoverse-850 px-4 py-3">
<Link
href={CYPHER_CARD_URL}
target="_blank"
rel="noopener noreferrer"
referrerPolicy="no-referrer"
>
<div className="relative flex h-[4.5rem] w-full flex-1 items-center gap-2 rounded-2xl bg-osmoverse-850 px-4 py-3">
<div className="mr-3 flex h-12 w-12 flex-col items-center justify-center rounded-xl bg-osmoverse-800">
<Icon id="cypher-card" />
</div>
Expand All @@ -21,9 +31,20 @@ export const CypherCard = () => {
{t("cypherCard.cypherOrder")}
</p>
</div>
<div className="caption ml-auto rounded-xl bg-osmoverse-800 p-1 px-2 text-osmoverse-300">

<div className="caption rounded-xl bg-osmoverse-800 p-1 px-2 text-osmoverse-300">
{t("cypherCard.cypherBeta")}
</div>

<button
onClick={(e) => {
e.preventDefault();
setIsClosed(true);
}}
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 text-osmoverse-300 hover:text-white-full"
>
<Icon id="close" width={16} height={16} />
</button>
</div>
</Link>
);
Expand Down
7 changes: 0 additions & 7 deletions packages/web/components/complex/portfolio/portfolio-dust.ts

This file was deleted.

34 changes: 13 additions & 21 deletions packages/web/components/complex/portfolio/portfolio-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react";
import classNames from "classnames";
import { observer } from "mobx-react-lite";
import { FunctionComponent } from "react";
import { useLocalStorage } from "react-use";

import { Allocation } from "~/components/complex/portfolio/allocation";
import { AssetsOverview } from "~/components/complex/portfolio/assets-overview";
Expand All @@ -22,19 +21,20 @@ import {
useWalletSelect,
} from "~/hooks";
import { useStore } from "~/stores";
import { HideDustState } from "~/stores/user-settings/hide-dust";
import { api } from "~/utils/trpc";

import { CypherCard } from "./cypher-card";
import { GetStartedWithOsmosis } from "./get-started-with-osmosis";
import { PORTFOLIO_HIDE_DUST_KEY } from "./portfolio-dust";

export const PortfolioPage: FunctionComponent = observer(() => {
const { t } = useTranslation();
const { accountStore } = useStore();
const { accountStore, userSettings } = useStore();
const wallet = accountStore.getWallet(accountStore.osmosisChainId);
const { isLoading: isWalletLoading } = useWalletSelect();
const featureFlags = useFeatureFlags();

useAmplitudeAnalytics({
const { logEvent } = useAmplitudeAnalytics({
onLoadEvent: [EventName.Portfolio.pageViewed],
});

Expand All @@ -53,25 +53,18 @@ export const PortfolioPage: FunctionComponent = observer(() => {

const totalCap = allocation?.totalCap;

const userHasNoAssets = allocation && totalCap?.toDec()?.isZero();
const userHasNoAssets = Boolean(allocation && totalCap?.toDec()?.isZero());

const [overviewRef, { height: overviewHeight }] =
useDimension<HTMLDivElement>();
const [tabsRef, { height: tabsHeight }] = useDimension<HTMLDivElement>();

const { logEvent } = useAmplitudeAnalytics();

const isWalletConnected =
wallet && wallet.isWalletConnected && wallet.address;

const { isLoading: isWalletLoading } = useWalletSelect();

const [hideDust, setHideDust] = useLocalStorage(
PORTFOLIO_HIDE_DUST_KEY,
true
);
const showZeroBalancesSplash =
userHasNoAssets === true || userHasNoAssets === undefined;
const hideDustSettingStore =
userSettings.getUserSettingById<HideDustState>("hide-dust");
const hideDust = Boolean(hideDustSettingStore?.state?.hideDust);

return (
<div className="flex justify-center p-8 pt-4 1.5xl:flex-col md:p-4">
Expand Down Expand Up @@ -130,14 +123,16 @@ export const PortfolioPage: FunctionComponent = observer(() => {
<div className="mx-auto my-6 w-fit">
<Spinner />
</div>
) : showZeroBalancesSplash ? (
) : userHasNoAssets ? (
<UserZeroBalanceTableSplash />
) : (
<TabPanels>
<TabPanel>
<PortfolioAssetBalancesTable
hideDust={Boolean(hideDust)}
setHideDust={setHideDust}
setHideDust={(hideDust) =>
hideDustSettingStore?.setState({ hideDust })
}
tableTopPadding={overviewHeight + tabsHeight}
/>
</TabPanel>
Expand Down Expand Up @@ -166,10 +161,7 @@ export const PortfolioPage: FunctionComponent = observer(() => {
>
{featureFlags.cypherCard && <CypherCard />}
{!isLoadingAllocation && !userHasNoAssets && (
<Allocation
allocation={allocation}
hideDust={Boolean(hideDust)}
/>
<Allocation allocation={allocation} />
)}
</div>
<div className="flex w-full flex-col">
Expand Down
29 changes: 24 additions & 5 deletions packages/web/components/layouts/main.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import classNames from "classnames";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
import React, { type FunctionComponent, type PropsWithChildren } from "react";
import React, {
type FunctionComponent,
type PropsWithChildren,
useMemo,
} from "react";

import { IconButton } from "~/components/buttons/icon-button";
import { MainLayoutMenu, MainMenu } from "~/components/main-menu";
Expand Down Expand Up @@ -33,9 +37,24 @@ export const MainLayout = observer(
const showFixedLogo = !smallVerticalScreen && !isMobile;
const showBlockLogo = smallVerticalScreen && !isMobile;

const selectedMenuItem = menus.find(
({ selectionTest }) => selectionTest?.test(router.pathname) ?? false
const selectedMenuItem = useMemo(
() =>
menus.find(
({ selectionTest }) => selectionTest?.test(router.pathname) ?? false
),
[menus, router.pathname]
);
const navBarTitle = useMemo(() => {
// Note: in designs we're moving to no title in nav bar.
// Filtering here to avoid title bar flash from menu list item titles
// appearing in nav bar before child components set global state via useNavBar.
const selectedTitle = selectedMenuItem?.label;

if (selectedTitle === "Trade") return;
if (selectedTitle === "Portfolio") return;

return selectedTitle;
}, [selectedMenuItem?.label]);

useHasAssetVariants();

Expand All @@ -46,7 +65,7 @@ export const MainLayout = observer(
<OsmosisFullLogo onClick={() => router.push("/")} />
</div>
)}
<div className="fixed inset-y-0 z-40 flex w-sidebar flex-col overflow-y-auto overflow-x-hidden px-2 py-6 xl:bg-osmoverse-1000 md:hidden">
<div className="fixed inset-y-0 z-40 flex w-sidebar flex-col overflow-y-auto overflow-x-hidden bg-osmoverse-1000 px-2 py-6 md:hidden">
{showBlockLogo && (
<div className="z-50 mx-auto ml-3 w-sidebar grow-0">
<OsmosisFullLogo onClick={() => router.push("/")} />
Expand All @@ -66,7 +85,7 @@ export const MainLayout = observer(
</div>
<NavBar
className="ml-sidebar md:ml-0"
title={selectedMenuItem?.label ?? ""}
title={navBarTitle}
menus={menus}
secondaryMenuItems={secondaryMenuItems}
/>
Expand Down
Loading

0 comments on commit 414bf41

Please sign in to comment.