Skip to content

Commit

Permalink
Merge branch 'main' into refresh-voting-power-from-details
Browse files Browse the repository at this point in the history
  • Loading branch information
mstrasinskis authored Dec 13, 2024
2 parents 8e253a7 + 8a1e3c8 commit 1e95656
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
shouldDisplayRewardLossNotification,
} from "$lib/utils/neuron.utils";
import {
IconCheckCircle,
IconCheckCircleFill,
IconError,
IconWarning,
} from "@dfinity/gix-components";
Expand All @@ -17,6 +17,8 @@
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import FollowNeuronsButton from "./actions/FollowNeuronsButton.svelte";
import ConfirmFollowingButton from "./actions/ConfirmFollowingButton.svelte";
import { secondsToDissolveDelayDuration } from "$lib/utils/date.utils";
import { START_REDUCING_VOTING_POWER_AFTER_SECONDS } from "$lib/constants/neurons.constants";
export let neuron: NeuronInfo;
Expand All @@ -30,14 +32,13 @@
$: isLosingRewardsSoon =
!isLosingRewards && shouldDisplayRewardLossNotification(neuron);
let icon: typeof IconError | typeof IconWarning | typeof IconCheckCircle;
let icon: typeof IconError | typeof IconWarning | typeof IconCheckCircleFill;
$: icon =
isFollowingReset || isLosingRewards
? IconError
: isLosingRewardsSoon
? IconWarning
: // TODO(mstr): Replace with the filled version.
IconCheckCircle;
: IconCheckCircleFill;
let title: string;
$: title =
Expand Down Expand Up @@ -65,9 +66,19 @@
}
);
};
const tooltipText = replacePlaceholders($i18n.losing_rewards.description, {
$period: secondsToDissolveDelayDuration(
BigInt(START_REDUCING_VOTING_POWER_AFTER_SECONDS)
),
});
</script>

<CommonItemAction testId="nns-neuron-reward-status-action-component">
<CommonItemAction
testId="nns-neuron-reward-status-action-component"
{tooltipText}
tooltipId="neuron-reward-status-icon"
>
<span
slot="icon"
class="icon"
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/lib/components/staking/ProjectStakeCell.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script lang="ts">
import AmountDisplay from "$lib/components/ic/AmountDisplay.svelte";
import AmountWithUsd from "$lib/components/ic/AmountWithUsd.svelte";
import { ENABLE_USD_VALUES_FOR_NEURONS } from "$lib/stores/feature-flags.store";
import type { TableProject } from "$lib/types/staking";
import { TokenAmountV2 } from "@dfinity/utils";
Expand All @@ -8,6 +10,11 @@

<div data-tid="project-stake-cell-component">
{#if !(rowData.stake instanceof TokenAmountV2) || rowData.stake.toUlps() > 0}
<AmountDisplay singleLine amount={rowData.stake} />
{#if $ENABLE_USD_VALUES_FOR_NEURONS}
<AmountWithUsd amount={rowData.stake} amountInUsd={rowData.stakeInUsd}
></AmountWithUsd>
{:else}
<AmountDisplay singleLine amount={rowData.stake} />
{/if}
{/if}
</div>
8 changes: 8 additions & 0 deletions frontend/src/lib/components/staking/ProjectsTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import ProjectTitleCell from "$lib/components/staking/ProjectTitleCell.svelte";
import ResponsiveTable from "$lib/components/ui/ResponsiveTable.svelte";
import { authSignedInStore } from "$lib/derived/auth.derived";
import { icpSwapUsdPricesStore } from "$lib/derived/icp-swap.derived";
import { selectableUniversesStore } from "$lib/derived/selectable-universes.derived";
import { loadIcpSwapTickers } from "$lib/services/icp-swap.services";
import { ENABLE_USD_VALUES_FOR_NEURONS } from "$lib/stores/feature-flags.store";
import { i18n } from "$lib/stores/i18n";
import { neuronsStore } from "$lib/stores/neurons.store";
import { snsNeuronsStore } from "$lib/stores/sns-neurons.store";
Expand All @@ -17,6 +20,10 @@
} from "$lib/utils/staking.utils";
import { createEventDispatcher } from "svelte";
$: if ($authSignedInStore && $ENABLE_USD_VALUES_FOR_NEURONS) {
loadIcpSwapTickers();
}
const columns: ProjectsTableColumn[] = [
{
title: $i18n.staking.nervous_systems,
Expand Down Expand Up @@ -71,6 +78,7 @@
isSignedIn: $authSignedInStore,
nnsNeurons: $neuronsStore?.neurons,
snsNeurons: $snsNeuronsStore,
icpSwapUsdPrices: $icpSwapUsdPricesStore,
});
let sortedTableProjects: TableProject[];
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/utils/staking.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const getTableProjects = ({
isSignedIn: boolean;
nnsNeurons: NeuronInfo[] | undefined;
snsNeurons: { [rootCanisterId: string]: { neurons: SnsNeuron[] } };
icpSwapUsdPrices?: IcpSwapUsdPricesStoreData;
icpSwapUsdPrices: IcpSwapUsdPricesStoreData;
}): TableProject[] => {
return universes.map((universe) => {
const token =
Expand Down
87 changes: 87 additions & 0 deletions frontend/src/tests/lib/components/staking/ProjectsTable.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import * as icpSwapApi from "$lib/api/icp-swap.api";
import ProjectsTable from "$lib/components/staking/ProjectsTable.svelte";
import { OWN_CANISTER_ID_TEXT } from "$lib/constants/canister-ids.constants";
import { CKUSDC_UNIVERSE_CANISTER_ID } from "$lib/constants/ckusdc-canister-ids.constants";
import { AppPath } from "$lib/constants/routes.constants";
import { overrideFeatureFlagsStore } from "$lib/stores/feature-flags.store";
import { icpSwapTickersStore } from "$lib/stores/icp-swap.store";
import { neuronsStore } from "$lib/stores/neurons.store";
import { snsNeuronsStore } from "$lib/stores/sns-neurons.store";
import { page } from "$mocks/$app/stores";
import { resetIdentity, setNoIdentity } from "$tests/mocks/auth.store.mock";
import { mockIcpSwapTicker } from "$tests/mocks/icp-swap.mock";
import { mockNeuron } from "$tests/mocks/neurons.mock";
import { createMockSnsNeuron } from "$tests/mocks/sns-neurons.mock";
import { mockSnsToken, principal } from "$tests/mocks/sns-projects.mock";
Expand All @@ -14,6 +19,7 @@ import { resetSnsProjects, setSnsProjects } from "$tests/utils/sns.test-utils";
import { render } from "$tests/utils/svelte.test-utils";
import { runResolvedPromises } from "$tests/utils/timers.test-utils";
import { nonNullish } from "@dfinity/utils";
import { get } from "svelte/store";

describe("ProjectsTable", () => {
const snsTitle = "SNS-1";
Expand Down Expand Up @@ -444,6 +450,87 @@ describe("ProjectsTable", () => {
await rowPos[1].click();
expect(onNnsStakeTokens).not.toBeCalled();
});

it("should display stake in USD", async () => {
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", true);

const tickers = [
{
...mockIcpSwapTicker,
base_id: CKUSDC_UNIVERSE_CANISTER_ID.toText(),
last_price: "10.00",
},
];
vi.spyOn(icpSwapApi, "queryIcpSwapTickers").mockResolvedValue(tickers);

neuronsStore.setNeurons({
neurons: [nnsNeuronWithStake],
certified: true,
});

expect(get(icpSwapTickersStore)).toBeUndefined();
expect(icpSwapApi.queryIcpSwapTickers).toBeCalledTimes(0);

const po = renderComponent();
await runResolvedPromises();

expect(get(icpSwapTickersStore)).toEqual(tickers);
expect(icpSwapApi.queryIcpSwapTickers).toBeCalledTimes(1);

const rowPos = await po.getProjectsTableRowPos();
expect(await rowPos[0].getStake()).toBe("1.00 ICP");
expect(await rowPos[0].getStakeInUsd()).toBe("$10.00");
expect(await rowPos[0].hasStakeInUsd()).toBe(true);
});

it("should not load ICP Swap tickers without feature flag", async () => {
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", false);

vi.spyOn(icpSwapApi, "queryIcpSwapTickers").mockResolvedValue([]);

neuronsStore.setNeurons({
neurons: [nnsNeuronWithStake],
certified: true,
});

expect(get(icpSwapTickersStore)).toBeUndefined();
expect(icpSwapApi.queryIcpSwapTickers).toBeCalledTimes(0);

const po = renderComponent();
await runResolvedPromises();

expect(get(icpSwapTickersStore)).toBeUndefined();
expect(icpSwapApi.queryIcpSwapTickers).toBeCalledTimes(0);

const rowPos = await po.getProjectsTableRowPos();
expect(await rowPos[0].getStake()).toBe("1.00 ICP");
expect(await rowPos[0].hasStakeInUsd()).toBe(false);
});

it("should not load ICP Swap tickers when not signed in", async () => {
setNoIdentity();
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", true);

vi.spyOn(icpSwapApi, "queryIcpSwapTickers").mockResolvedValue([]);

neuronsStore.setNeurons({
neurons: [nnsNeuronWithStake],
certified: true,
});

expect(get(icpSwapTickersStore)).toBeUndefined();
expect(icpSwapApi.queryIcpSwapTickers).toBeCalledTimes(0);

const po = renderComponent();
await runResolvedPromises();

expect(get(icpSwapTickersStore)).toBeUndefined();
expect(icpSwapApi.queryIcpSwapTickers).toBeCalledTimes(0);

const rowPos = await po.getProjectsTableRowPos();
expect(await rowPos[0].getStake()).toBe("-/- ICP");
expect(await rowPos[0].getStakeInUsd()).toBe("$-/-");
});
});

it("should update table when universes store changes", async () => {
Expand Down
20 changes: 19 additions & 1 deletion frontend/src/tests/page-objects/ProjectStakeCell.page-object.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { AmountDisplayPo } from "$tests/page-objects/AmountDisplay.page-object";
import { AmountWithUsdPo } from "$tests/page-objects/AmountWithUsd.page-object";
import { BasePageObject } from "$tests/page-objects/base.page-object";
import type { PageObjectElement } from "$tests/types/page-object.types";

Expand All @@ -8,7 +10,23 @@ export class ProjectStakeCellPo extends BasePageObject {
return new ProjectStakeCellPo(element.byTestId(ProjectStakeCellPo.TID));
}

getAmountWithUsdPo(): AmountWithUsdPo {
return AmountWithUsdPo.under(this.root);
}

getAmountDisplayPo(): AmountDisplayPo {
return AmountDisplayPo.under(this.root);
}

async getStake(): Promise<string> {
return await this.getText();
return (await this.getAmountDisplayPo().getText()) ?? "";
}

async getStakeInUsd(): Promise<string> {
return await this.getAmountWithUsdPo().getAmountInUsd();
}

async hasStakeInUsd(): Promise<boolean> {
return await this.getAmountWithUsdPo().isPresent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export class ProjectsTableRowPo extends ResponsiveTableRowPo {
return this.getProjectStakeCellPo().getStake();
}

getStakeInUsd(): Promise<string> {
return this.getProjectStakeCellPo().getStakeInUsd();
}

hasStakeInUsd(): Promise<boolean> {
return this.getProjectStakeCellPo().hasStakeInUsd();
}

getNeuronCount(): Promise<string> {
return this.getProjectNeuronsCellPo().getNeuronCount();
}
Expand Down

0 comments on commit 1e95656

Please sign in to comment.