Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into NNS1-3480/export-menu…
Browse files Browse the repository at this point in the history
…-button-to-account-menu
  • Loading branch information
yhabib committed Dec 6, 2024
2 parents 9fccc3e + d516f88 commit 0c4edb3
Show file tree
Hide file tree
Showing 20 changed files with 625 additions and 34 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG-Sns_Aggregator.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The SNS Aggregator is released through proposals in the Network Nervous System.

### Security

## [Proposal 132124](https://dashboard.internetcomputer.org/proposal/132124)
### Security
- Decoding quota of 10,000 in the `http_request` method.

## [Proposal 129614](https://dashboard.internetcomputer.org/proposal/129614)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import { formatTokenV2 } from "$lib/utils/token.utils";
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import type { Account } from "$lib/types/account";
import type { Readable } from "svelte/store";
const dispatcher = createEventDispatcher<{
nnsExportIcpTransactionsCsvTriggered: void;
Expand All @@ -45,13 +46,13 @@
let swapCanisterAccounts: Set<string>;
let neuronAccounts: Set<string>;
let nnsAccounts: Account[];
let swapCanisterAccountsStore: Readable<Set<string>> | undefined;
$: identity = $authStore.identity;
$: neuronAccounts = $neuronAccountsStore;
$: nnsAccounts = $nnsAccountsListStore;
$: isDisabled = isNullish(identity) || nnsAccounts.length === 0;
const swapCanisterAccountsStore = createSwapCanisterAccountsStore(
$: swapCanisterAccountsStore = createSwapCanisterAccountsStore(
identity?.getPrincipal()
);
$: swapCanisterAccounts = $swapCanisterAccountsStore ?? new Set();
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/lib/components/neurons/LosingRewardsBanner.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { START_REDUCING_VOTING_POWER_AFTER_SECONDS } from "$lib/constants/neurons.constants";
import { secondsToDissolveDelayDuration } from "$lib/utils/date.utils";
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
import LosingRewardNeuronsModal from "$lib/modals/neurons/LosingRewardNeuronsModal.svelte";
// The neurons in the store are sorted by the time they will lose rewards.
let mostInactiveNeuron: NeuronInfo | undefined;
Expand All @@ -35,9 +36,7 @@
),
});
const onConfirm = () => {
// TODO: Display the modal
};
let isModalVisible = false;
</script>

<TestIdWrapper testId="losing-rewards-banner-component">
Expand All @@ -47,10 +46,17 @@
<IconInfo />
</BannerIcon>
<div slot="actions">
<button class="danger" on:click={onConfirm}
<button
data-tid="confirm-button"
class="danger"
on:click={() => (isModalVisible = true)}
>{$i18n.losing_rewards_banner.confirm}</button
>
</div>
</Banner>
{/if}

{#if isModalVisible}
<LosingRewardNeuronsModal on:nnsClose={() => (isModalVisible = false)} />
{/if}
</TestIdWrapper>
5 changes: 4 additions & 1 deletion frontend/src/lib/components/ui/NeuronTag.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
export let tag: NeuronTagData;
export let size: "medium" | "large" = "medium";
let intent: "error" | "info";
$: intent = tag.status === "danger" ? "error" : "info";
</script>

<Tag testId="neuron-tag-component" {size}>{tag.text}</Tag>
<Tag testId="neuron-tag-component" {size} {intent}>{tag.text}</Tag>
133 changes: 133 additions & 0 deletions frontend/src/lib/components/ui/UsdValueBanner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<script lang="ts">
import IC_LOGO_ROUNDED from "$lib/assets/icp-rounded.svg";
import TooltipIcon from "$lib/components/ui/TooltipIcon.svelte";
import { LEDGER_CANISTER_ID } from "$lib/constants/canister-ids.constants";
import { icpSwapUsdPricesStore } from "$lib/derived/icp-swap.derived";
import { i18n } from "$lib/stores/i18n";
import { formatNumber } from "$lib/utils/format.utils";
import { nonNullish } from "@dfinity/utils";
export let usdAmount: number | undefined;
const absentValue = "-/-";
let usdAmountFormatted: string;
$: usdAmountFormatted = nonNullish(usdAmount)
? formatNumber(usdAmount)
: absentValue;
let icpPrice: number | undefined;
$: icpPrice = $icpSwapUsdPricesStore?.[LEDGER_CANISTER_ID.toText()];
let icpPriceFormatted: string;
$: icpPriceFormatted = nonNullish(icpPrice)
? formatNumber(icpPrice)
: absentValue;
let icpAmount: number | undefined;
$: icpAmount = icpPrice && usdAmount && usdAmount / icpPrice;
let icpAmountFormatted: string;
$: icpAmountFormatted = nonNullish(icpAmount)
? formatNumber(icpAmount)
: absentValue;
</script>

<div class="wrapper" data-tid="usd-value-banner-component">
<div class="table-banner-icon">
<slot name="icon" />
</div>
<div class="text-content">
<div class="totals">
<h1 class="primary-amount" data-tid="primary-amount">
${usdAmountFormatted}
</h1>
<div class="secondary-amount" data-tid="secondary-amount">
{icpAmountFormatted}
{$i18n.core.icp}
</div>
</div>
<div class="exchange-rate">
<img
src={IC_LOGO_ROUNDED}
alt={$i18n.auth.ic_logo}
class="icp-icon desktop-only"
/>
<span class="desktop-only">
1 {$i18n.core.icp} = $<span data-tid="icp-price"
>{icpPriceFormatted}</span
>
</span>
<TooltipIcon>
<div class="mobile-only">
1 {$i18n.core.icp} = ${icpPriceFormatted}
</div>
<div>
{$i18n.accounts.token_price_source}
</div>
</TooltipIcon>
</div>
</div>
</div>

<style lang="scss">
@use "@dfinity/gix-components/dist/styles/mixins/media";
.wrapper {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--padding-2x);
background-color: var(--card-background-tint);
padding: var(--padding-2x);
border-radius: var(--padding);
border: 1.5px solid var(--card-border);
.table-banner-icon {
display: flex;
width: 72px;
}
.text-content {
flex-grow: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.totals {
.primary-amount {
margin: 0;
}
display: flex;
flex-direction: column;
}
.exchange-rate {
display: flex;
align-items: center;
gap: var(--padding-0_5x);
.icp-icon {
width: 20px;
height: 20px;
}
}
}
}
.desktop-only {
display: none;
}
@include media.min-width(medium) {
.desktop-only {
display: initial;
}
.mobile-only {
display: none;
}
}
</style>
7 changes: 6 additions & 1 deletion frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@
"received_amount": "Received Amount",
"received_amount_notice": "Received Amount *",
"transaction_time": "Transaction Time",
"transaction_time_seconds": "Seconds"
"transaction_time_seconds": "Seconds",
"token_price_source": "Token prices are provided by ICPSwap."
},
"neuron_types": {
"seed": "Seed",
Expand Down Expand Up @@ -383,6 +384,10 @@
},
"losing_rewards_modal": {
"goto_neuron": "Go to neuron details",
"title": "Review your following for neurons",
"description": "ICP neurons that are inactive for $period start losing voting rewards. In order to avoid losing rewards, vote manually, edit or confirm your following.",
"label": "Neurons",
"confirm": "Confirm Following",
"no_following": "This neuron has no following configured."
},
"new_followee": {
Expand Down
79 changes: 79 additions & 0 deletions frontend/src/lib/modals/neurons/LosingRewardNeuronsModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<script lang="ts">
import { i18n } from "$lib/stores/i18n";
import { Modal } from "@dfinity/gix-components";
import { createEventDispatcher, onMount } from "svelte";
import { secondsToDissolveDelayDuration } from "$lib/utils/date.utils";
import { START_REDUCING_VOTING_POWER_AFTER_SECONDS } from "$lib/constants/neurons.constants";
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import { soonLosingRewardNeuronsStore } from "$lib/derived/neurons.derived";
import NnsLosingRewardsNeuronCard from "$lib/components/neurons/NnsLosingRewardsNeuronCard.svelte";
import { listKnownNeurons } from "$lib/services/known-neurons.services";
const dispatcher = createEventDispatcher();
// Load KnownNeurons which are used in the FollowNnsTopicSections
onMount(() => listKnownNeurons());
const confirm = () => {
// TBD
};
</script>

<Modal on:nnsClose testId="losing-reward-neurons-modal-component">
<svelte:fragment slot="title">
{$i18n.losing_rewards_modal.title}
</svelte:fragment>

<div class="wrapper">
<p class="description">
{replacePlaceholders($i18n.losing_rewards_modal.description, {
$period: secondsToDissolveDelayDuration(
BigInt(START_REDUCING_VOTING_POWER_AFTER_SECONDS)
),
})}
</p>

<h3 class="label">{$i18n.losing_rewards_modal.label}</h3>
<ul class="cards">
{#each $soonLosingRewardNeuronsStore as neuron (neuron.neuronId)}
<li>
<NnsLosingRewardsNeuronCard {neuron} />
</li>
{/each}
</ul>
<div class="toolbar">
<button
on:click={() => dispatcher("nnsClose")}
class="secondary"
data-tid="cancel-button">{$i18n.core.cancel}</button
>
<button on:click={confirm} class="primary" data-tid="confirm-button"
>{$i18n.losing_rewards_modal.confirm}</button
>
</div>
</div>
</Modal>

<style lang="scss">
@use "@dfinity/gix-components/dist/styles/mixins/fonts";
.description {
margin: 0 0 var(--padding-3x);
}
.label {
margin-bottom: var(--padding);
@include fonts.standard(false);
color: var(--description-color);
}
.cards {
display: flex;
flex-direction: column;
gap: var(--padding);
margin-bottom: var(--padding-3x);
padding: 0;
list-style-type: none;
}
</style>
5 changes: 5 additions & 0 deletions frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ interface I18nAccounts {
received_amount_notice: string;
transaction_time: string;
transaction_time_seconds: string;
token_price_source: string;
}

interface I18nNeuron_types {
Expand Down Expand Up @@ -399,6 +400,10 @@ interface I18nLosing_rewards_banner {

interface I18nLosing_rewards_modal {
goto_neuron: string;
title: string;
description: string;
label: string;
confirm: string;
no_following: string;
}

Expand Down
21 changes: 11 additions & 10 deletions frontend/src/lib/utils/icp-transactions.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ICPToken,
TokenAmountV2,
fromNullable,
isNullish,
nonNullish,
} from "@dfinity/utils";
import { transactionName } from "./transactions.utils";
Expand Down Expand Up @@ -122,9 +123,7 @@ const getTransactionInformation = (
data = operation.Transfer;
}
// Edge case, a transaction will have either "Approve", "Burn", "Mint" or "Transfer" data.
if (data === undefined) {
throw new Error(`Unknown transaction type ${JSON.stringify(operation)}`);
}
if (isNullish(data)) return undefined;

return {
from: "from" in data ? data.from : undefined,
Expand All @@ -149,18 +148,20 @@ export const mapIcpTransactionToReport = ({
swapCanisterAccounts: Set<string>;
}) => {
const txInfo = getTransactionInformation(transaction.transaction.operation);
if (txInfo === undefined) {
if (isNullish(txInfo)) {
throw new Error(
`Unknown transaction type ${
`Unknown transaction type "${
Object.keys(transaction.transaction.operation)[0]
}`
}"`
);
}

const { to, from, amount, fee } = txInfo;
const isSelfTransaction = isToSelf(transaction.transaction);
const isReceive = isSelfTransaction || from !== accountIdentifier;
const transactionDirection = isReceive ? "credit" : "debit";
const transactionDirection: "credit" | "debit" = isReceive
? "credit"
: "debit";

const useFee = !isReceive;
const feeApplied = useFee && fee ? fee : 0n;
Expand Down Expand Up @@ -212,11 +213,11 @@ export const mapIcpTransactionToUi = ({
}): UiTransaction | undefined => {
try {
const txInfo = getTransactionInformation(transaction.transaction.operation);
if (txInfo === undefined) {
if (isNullish(txInfo)) {
throw new Error(
`Unknown transaction type ${
`Unknown transaction type "${
Object.keys(transaction.transaction.operation)[0]
}`
}"`
);
}
const isReceive =
Expand Down
Loading

0 comments on commit 0c4edb3

Please sign in to comment.