Skip to content

Commit d847d8b

Browse files
authored
feat(projects): separate projects tables by type (#7122)
# Motivation The Tokens and Neurons tables will be divided into multiple tables to distinguish ICP from other assets. This PR separates the Neurons table into distinct tables for each type: NNS and SNS neurons. The SNS neuron table will be hidden if the user has no neurons and has selected the hide 0 balance toggle. Additionally, we want to display a different table for those SNS projects that have been sunset. | Mobile | Desktop | |--------|--------| |<img width="537" height="991" alt="Screenshot 2025-07-17 at 15 58 15" src="https://github.com/user-attachments/assets/c5f62d67-3db3-45ed-a30c-8e196b65381d" /> | <img width="1272" height="760" alt="Screenshot 2025-07-17 at 15 58 02" src="https://github.com/user-attachments/assets/434e073d-ff94-4735-90b4-667acac4a9bb" /> | | <img width="467" height="386" alt="Screenshot 2025-07-17 at 17 23 44" src="https://github.com/user-attachments/assets/d14f0029-82ab-40a0-8b08-45fcdea759bd" /> | <img width="979" height="703" alt="Screenshot 2025-07-17 at 17 23 21" src="https://github.com/user-attachments/assets/8380a78f-a439-4996-94ca-40311d31ebcb" /> | [NNS1-3966](https://dfinity.atlassian.net/browse/NNS1-3966) # Changes - Split the current Projects table into three instances: nns, sns and sunset sns. # Tests - [DevEnv](https://qsgjb-riaaa-aaaaa-aaaga-cai.yhabib-ingress.devenv.dfinity.network/tokens/) # Todos - [x] Accessibility (a11y) – any impact? - [x] Changelog – is it needed? [NNS1-3966]: https://dfinity.atlassian.net/browse/NNS1-3966?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent fdcf69c commit d847d8b

File tree

5 files changed

+160
-49
lines changed

5 files changed

+160
-49
lines changed

frontend/src/lib/components/staking/ProjectsTable.svelte

Lines changed: 151 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
import ResponsiveTable from "$lib/components/ui/ResponsiveTable.svelte";
99
import Separator from "$lib/components/ui/Separator.svelte";
1010
import UsdValueBanner from "$lib/components/ui/UsdValueBanner.svelte";
11-
import { OWN_CANISTER_ID_TEXT } from "$lib/constants/canister-ids.constants";
11+
import {
12+
abandonedProjectsCanisterId,
13+
OWN_CANISTER_ID_TEXT,
14+
} from "$lib/constants/canister-ids.constants";
1215
import { authSignedInStore } from "$lib/derived/auth.derived";
1316
import { icpSwapUsdPricesStore } from "$lib/derived/icp-swap.derived";
1417
import { selectableUniversesStore } from "$lib/derived/selectable-universes.derived";
1518
import { loadIcpSwapTickers } from "$lib/services/icp-swap.services";
1619
import { failedActionableSnsesStore } from "$lib/stores/actionable-sns-proposals.store";
20+
import { ENABLE_NEW_TABLES } from "$lib/stores/feature-flags.store";
1721
import { hideZeroNeuronsStore } from "$lib/stores/hide-zero-neurons.store";
1822
import { i18n } from "$lib/stores/i18n";
1923
import { neuronsStore } from "$lib/stores/neurons.store";
@@ -29,65 +33,96 @@
2933
sortTableProjects,
3034
} from "$lib/utils/staking.utils";
3135
import { IconNeuronsPage } from "@dfinity/gix-components";
32-
import { TokenAmountV2, isNullish } from "@dfinity/utils";
36+
import { isNullish, TokenAmountV2 } from "@dfinity/utils";
3337
import { createEventDispatcher } from "svelte";
3438
3539
$: if ($authSignedInStore) {
3640
loadIcpSwapTickers();
3741
}
38-
39-
let columns: ProjectsTableColumn[] = [];
40-
$: columns = [
41-
{
42-
id: "title",
43-
title: $i18n.staking.nervous_systems,
44-
cellComponent: ProjectTitleCell,
45-
alignment: "left",
46-
templateColumns: ["minmax(min-content, max-content)"],
47-
comparator: $authSignedInStore ? compareByProject : undefined,
48-
},
49-
{
50-
title: "",
51-
alignment: "left",
52-
templateColumns: ["1fr"],
53-
},
42+
let commonColumns: ProjectsTableColumn[] = [];
43+
$: commonColumns = [
5444
{
5545
id: "stake",
5646
title: $i18n.neuron_detail.stake,
5747
cellComponent: ProjectStakeCell,
5848
alignment: "right",
59-
templateColumns: ["max-content"],
60-
comparator: $authSignedInStore ? compareByStake : undefined,
61-
},
62-
{
63-
title: "",
64-
alignment: "left",
6549
templateColumns: ["1fr"],
50+
comparator: $authSignedInStore ? compareByStake : undefined,
6651
},
6752
{
6853
title: $i18n.neuron_detail.maturity_title,
6954
cellComponent: ProjectMaturityCell,
7055
alignment: "right",
71-
templateColumns: ["max-content"],
72-
},
73-
{
74-
title: "",
75-
alignment: "left",
7656
templateColumns: ["1fr"],
7757
},
7858
{
7959
id: "neurons",
8060
title: $i18n.neurons.title,
8161
cellComponent: ProjectNeuronsCell,
8262
alignment: "right",
83-
templateColumns: ["max-content"],
63+
templateColumns: ["1fr"],
8464
comparator: $authSignedInStore ? compareByNeuron : undefined,
8565
},
8666
{
8767
title: "",
8868
cellComponent: ProjectActionsCell,
8969
alignment: "right",
90-
templateColumns: ["max-content"],
70+
templateColumns: ["1fr"],
71+
},
72+
];
73+
74+
let columns: ProjectsTableColumn[] = [];
75+
$: columns = [
76+
{
77+
id: "title",
78+
title: $i18n.staking.nervous_systems,
79+
cellComponent: ProjectTitleCell,
80+
alignment: "left",
81+
templateColumns: ["2fr"],
82+
comparator: $authSignedInStore ? compareByProject : undefined,
83+
},
84+
...commonColumns,
85+
];
86+
87+
let nnsColumns: ProjectsTableColumn[] = [];
88+
$: nnsColumns = [
89+
{
90+
id: "title",
91+
title: $i18n.staking.nervous_systems_nns,
92+
cellComponent: ProjectTitleCell,
93+
alignment: "left",
94+
templateColumns: ["2fr"],
95+
comparator: $authSignedInStore ? compareByProject : undefined,
96+
},
97+
...commonColumns,
98+
];
99+
100+
let snsColumns: ProjectsTableColumn[] = [];
101+
$: snsColumns = [
102+
{
103+
id: "title",
104+
title: $i18n.staking.nervous_systems_sns,
105+
cellComponent: ProjectTitleCell,
106+
alignment: "left",
107+
templateColumns: ["2fr"],
108+
comparator: $authSignedInStore ? compareByProject : undefined,
109+
},
110+
...commonColumns,
111+
];
112+
113+
let sunsettedSnsColumns: ProjectsTableColumn[] = [];
114+
$: sunsettedSnsColumns = [
115+
{
116+
id: "title",
117+
title: $i18n.staking.nervous_systems_sns_sunset,
118+
cellComponent: ProjectTitleCell,
119+
alignment: "left",
120+
templateColumns: ["2fr"],
121+
},
122+
{
123+
title: "",
124+
alignment: "left",
125+
templateColumns: ["1fr"],
91126
},
92127
];
93128
@@ -132,6 +167,21 @@
132167
(!("stakeInUsd" in project) || isNullish(project.stakeInUsd))
133168
);
134169
170+
let nnsNeurons: TableProject[] = [];
171+
$: nnsNeurons = sortedTableProjects.filter(
172+
(project) => project.universeId === OWN_CANISTER_ID_TEXT
173+
);
174+
175+
let snsNeurons: TableProject[] = [];
176+
$: snsNeurons = sortedTableProjects
177+
.filter((p) => p.universeId !== OWN_CANISTER_ID_TEXT)
178+
.filter((p) => !abandonedProjectsCanisterId.includes(p.universeId));
179+
180+
let sunsetSns: TableProject[] = [];
181+
$: sunsetSns = sortedTableProjects.filter((p) =>
182+
abandonedProjectsCanisterId.includes(p.universeId)
183+
);
184+
135185
const dispatcher = createEventDispatcher();
136186
137187
const handleAction = ({
@@ -154,7 +204,76 @@
154204
</UsdValueBanner>
155205
{/if}
156206

157-
{#if !$authSignedInStore}
207+
{#if $ENABLE_NEW_TABLES}
208+
{#if !$authSignedInStore}
209+
<ResponsiveTable
210+
tableData={nnsNeurons}
211+
columns={nnsColumns}
212+
on:nnsAction={handleAction}
213+
/>
214+
215+
<ResponsiveTable
216+
tableData={snsNeurons}
217+
columns={snsColumns}
218+
on:nnsAction={handleAction}
219+
/>
220+
221+
{#if sunsetSns.length > 0}
222+
<ResponsiveTable tableData={sunsetSns} columns={sunsettedSnsColumns} />
223+
{/if}
224+
{:else}
225+
<ResponsiveTable
226+
tableData={nnsNeurons}
227+
columns={nnsColumns}
228+
on:nnsAction={handleAction}
229+
bind:order={$projectsTableOrderStore}
230+
displayTableSettings
231+
>
232+
<svelte:fragment slot="settings-popover">
233+
<HideZeroNeuronsToggle />
234+
<Separator spacing="none" />
235+
</svelte:fragment>
236+
237+
<div
238+
slot="last-row"
239+
class="last-row"
240+
class:hidden={!shouldHideProjectsWithoutNeurons}
241+
>
242+
{#if shouldHideProjectsWithoutNeurons}
243+
<div class="show-all-button-container">
244+
{$i18n.staking.hide_no_neurons_table_hint}
245+
<button
246+
data-tid="show-all-button"
247+
class="ghost show-all"
248+
on:click={showAll}
249+
>
250+
{$i18n.staking.show_all}</button
251+
>
252+
</div>
253+
{/if}
254+
</div>
255+
</ResponsiveTable>
256+
257+
{#if snsNeurons.length > 0}
258+
<ResponsiveTable
259+
tableData={snsNeurons}
260+
columns={snsColumns}
261+
on:nnsAction={handleAction}
262+
bind:order={$projectsTableOrderStore}
263+
displayTableSettings
264+
>
265+
<svelte:fragment slot="settings-popover">
266+
<HideZeroNeuronsToggle />
267+
<Separator spacing="none" />
268+
</svelte:fragment>
269+
</ResponsiveTable>
270+
{/if}
271+
272+
{#if $hideZeroNeuronsStore !== "hide" && sunsetSns.length > 0}
273+
<ResponsiveTable tableData={sunsetSns} columns={sunsettedSnsColumns} />
274+
{/if}
275+
{/if}
276+
{:else if !$authSignedInStore}
158277
<ResponsiveTable
159278
tableData={sortedTableProjects}
160279
{columns}

frontend/src/lib/i18n/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@
338338
"title": "Stake and earn voting rewards",
339339
"text": "Earn voting rewards by staking your tokens in neurons. Neurons allow you to participate in governance of the Internet Computer, and DAOs deployed on the Internet Computer.",
340340
"nervous_systems": "Nervous Systems",
341+
"nervous_systems_nns": "NNS DAO",
342+
"nervous_systems_sns": "SNS DAOs",
343+
"nervous_systems_sns_sunset": "Sunset SNS DAOs",
341344
"hide_no_neurons_header": "Neurons",
342345
"hide_no_neurons_toggle_label": "Hide projects with 0 neurons",
343346
"hide_no_neurons_table_hint": "Projects with 0 neurons are hidden.",

frontend/src/lib/types/i18n.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@ interface I18nStaking {
351351
title: string;
352352
text: string;
353353
nervous_systems: string;
354+
nervous_systems_nns: string;
355+
nervous_systems_sns: string;
356+
nervous_systems_sns_sunset: string;
354357
hide_no_neurons_header: string;
355358
hide_no_neurons_toggle_label: string;
356359
hide_no_neurons_table_hint: string;

frontend/src/lib/types/responsive-table.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface ResponsiveTableRowData {
1111
export type ColumnAlignment = "left" | "right";
1212
export type TemplateItem =
1313
| "1fr"
14+
| "2fr"
1415
| "max-content"
1516
| "minmax(max-content, 1fr)"
1617
| "minmax(min-content, max-content)";

frontend/src/tests/lib/components/staking/ProjectsTable.spec.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,8 @@ describe("ProjectsTable", () => {
6868
const po = renderComponent();
6969
expect(await po.getDesktopColumnHeaders()).toEqual([
7070
"Nervous Systems",
71-
"",
7271
"Stake",
73-
"",
7472
"Maturity",
75-
"",
7673
"Neurons",
7774
"", // No header for actions column.
7875
]);
@@ -91,11 +88,8 @@ describe("ProjectsTable", () => {
9188
const rows = await po.getRows();
9289
expect(await rows[0].getCellAlignments()).toEqual([
9390
"desktop-align-left", // Nervous Systems
94-
expect.any(String), // gap
9591
"desktop-align-right", // Stake
96-
expect.any(String), // gap
9792
"desktop-align-right", // Maturity
98-
expect.any(String), // gap
9993
"desktop-align-right", // Neurons
10094
"desktop-align-right", // Actions
10195
]);
@@ -105,19 +99,10 @@ describe("ProjectsTable", () => {
10599
const po = renderComponent();
106100

107101
expect(await po.getDesktopGridTemplateColumns()).toBe(
108-
[
109-
"minmax(min-content, max-content)", // Nervous Systems
110-
"1fr", // gap
111-
"max-content", // Stake
112-
"1fr", // gap
113-
"max-content", // Maturity
114-
"1fr", // gap
115-
"max-content", // Neurons
116-
"max-content", // Actions
117-
].join(" ")
102+
["2fr", "1fr", "1fr", "1fr", "1fr"].join(" ")
118103
);
119104
expect(await po.getMobileGridTemplateAreas()).toBe(
120-
'"first-cell last-cell" "cell-1 cell-1" "cell-3 cell-3" "cell-5 cell-5"'
105+
'"first-cell last-cell" "cell-0 cell-0" "cell-1 cell-1" "cell-2 cell-2"'
121106
);
122107
});
123108

0 commit comments

Comments
 (0)