Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NNS1-3450: New Exports NNS Neurons Snapshot Component #5813

Merged
merged 49 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e12a28c
utility function to download csv
yhabib Nov 19, 2024
93b378d
export neurons button
yhabib Nov 19, 2024
758eb4d
adds missing file
yhabib Nov 19, 2024
5507458
enables feature in devenv
yhabib Nov 19, 2024
c881da9
clean slate
yhabib Nov 21, 2024
b40cf63
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 21, 2024
abdbab0
clean up
yhabib Nov 21, 2024
dc5eb4d
--wip-- [skip ci]
yhabib Nov 21, 2024
465dffd
clean up
yhabib Nov 21, 2024
fd9f3c2
wip
yhabib Nov 21, 2024
c36ac74
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 21, 2024
c9f2e1b
wip
yhabib Nov 21, 2024
9ef6c80
improve test title
yhabib Nov 21, 2024
a34c975
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 21, 2024
a3b82e2
--wip-- [skip ci]
yhabib Nov 22, 2024
5454f5e
more tests
yhabib Nov 22, 2024
459e7a3
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 22, 2024
614ed40
new fields
yhabib Nov 22, 2024
ea1d404
extend test
yhabib Nov 22, 2024
fa8bb37
extends csv function
yhabib Nov 22, 2024
e6151a5
Merge remote-tracking branch 'origin/NNS1-3450/csv-sections' into NNS…
yhabib Nov 22, 2024
3716fb5
fix missing thigns
yhabib Nov 22, 2024
f1d66b0
Merge remote-tracking branch 'origin/NNS1-3450/csv-sections' into NNS…
yhabib Nov 22, 2024
e52edda
car
yhabib Nov 22, 2024
1f9647b
fmt
yhabib Nov 22, 2024
9c6c875
--wip-- [skip ci]
yhabib Nov 25, 2024
0faebad
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 25, 2024
bd29df9
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 25, 2024
88a5c94
--wip-- [skip ci]
yhabib Nov 25, 2024
c1b6430
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 25, 2024
c668af2
introduce po for testing
yhabib Nov 25, 2024
2fb16c9
adds missing change
yhabib Nov 25, 2024
49f6147
fixes constant
yhabib Nov 25, 2024
01983aa
applies changes
yhabib Nov 25, 2024
662377a
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 25, 2024
7439ba5
fmt
yhabib Nov 25, 2024
7b88b66
remove UI
yhabib Nov 25, 2024
e2739b1
removes old imports
yhabib Nov 25, 2024
968d9d1
removes unused translations
yhabib Nov 25, 2024
fd400d1
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 25, 2024
d40d931
fix tests
yhabib Nov 25, 2024
c151a72
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 25, 2024
76b194f
car
yhabib Nov 26, 2024
aa1ca86
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 26, 2024
d79a547
consistent used of test asserstions
yhabib Nov 26, 2024
1d78e00
fmt
yhabib Nov 26, 2024
095c855
car
yhabib Nov 26, 2024
9c863b3
Merge remote-tracking branch 'origin/main' into NNS1-3450/export-neur…
yhabib Nov 26, 2024
a517656
adds tests and fixes bug
yhabib Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions frontend/src/lib/components/header/ExportNeuronsButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<script lang="ts">
import { i18n } from "$lib/stores/i18n";
import { IconDown } from "@dfinity/gix-components";
import { createEventDispatcher } from "svelte";
import { neuronsStore } from "$lib/stores/neurons.store";
import {
formatMaturity,
getStateInfo,
neuronAvailableMaturity,
neuronStake,
neuronStakedMaturity,
} from "$lib/utils/neuron.utils";
import {
ICPToken,
isNullish,
secondsToDuration,
TokenAmountV2,
} from "@dfinity/utils";
import { formatTokenV2 } from "$lib/utils/token.utils";
import { NeuronState, type NeuronInfo } from "@dfinity/nns";
import {
CsvGenerationError,
FileSystemAccessError,
generateCsvFileToSave,
} from "$lib/utils/export-to-csv.utils";
import { toastsError } from "$lib/stores/toasts.store";
import {
formatDateCompact,
getFutureDateFromDelayInSeconds,
nanoSecondsToDateTime,
nowInBigIntNanoSeconds,
secondsToDate,
} from "$lib/utils/date.utils";
import { authStore } from "$lib/stores/auth.store";

const dispatcher = createEventDispatcher<{
nnsExportNeuronsCsvTriggered: void;
}>();

let isDisabled = true;
let neurons: NeuronInfo[] = [];
$: neurons = $neuronsStore?.neurons ?? [];
yhabib marked this conversation as resolved.
Show resolved Hide resolved
$: nnsAccountPrincipal = $authStore.identity?.getPrincipal().toText();
$: isDisabled = neurons.length === 0 || isNullish(nnsAccountPrincipal);

const nnsNeuronToHumanReadableFormat = (neuron: NeuronInfo) => {
const controllerId = neuron.fullNeuron?.controller?.toString();
const neuronId = neuron.neuronId.toString();
const neuronAccountId = neuron.fullNeuron?.accountIdentifier.toString();
const stake = TokenAmountV2.fromUlps({
amount: neuronStake(neuron),
token: ICPToken,
});
const project = stake.token.name;
yhabib marked this conversation as resolved.
Show resolved Hide resolved
const symbol = stake.token.symbol;
const availableMaturity = neuronAvailableMaturity(neuron);
const stakedMaturity = neuronStakedMaturity(neuron);
const dissolveDelaySeconds = neuron.dissolveDelaySeconds;
const dissolveDate =
neuron.state === NeuronState.Dissolving
? getFutureDateFromDelayInSeconds(neuron.dissolveDelaySeconds)
: null;
const creationDate = secondsToDate(Number(neuron.createdTimestampSeconds));

return {
controllerId,
project,
symbol,
neuronId,
neuronAccountId,
stake: formatTokenV2({
value: stake,
detailed: true,
}),
availableMaturity: formatMaturity(availableMaturity),
stakedMaturity: formatMaturity(stakedMaturity),
dissolveDelaySeconds: secondsToDuration({
seconds: dissolveDelaySeconds,
i18n: $i18n.time,
}),
dissolveDate: dissolveDate ?? $i18n.core.not_applicable,
creationDate,
state: $i18n.neuron_state[getStateInfo(neuron.state).textKey],
};
};

const exportNeurons = async () => {
if (!nnsAccountPrincipal) return;

try {
const humanFriendlyContent = neurons.map(nnsNeuronToHumanReadableFormat);
const fileName = `neurons_export_${formatDateCompact(new Date())}`;
const metadataDate = nanoSecondsToDateTime(nowInBigIntNanoSeconds());

await generateCsvFileToSave({
data: humanFriendlyContent,
metadata: [
{
label: $i18n.export_csv_neurons.account_id_label,
value: nnsAccountPrincipal.toString(),
},
{
label: $i18n.export_csv_neurons.date_label,
value: metadataDate,
},
],
headers: [
{
id: "neuronId",
label: $i18n.export_csv_neurons.neuron_id,
},
{
id: "project",
label: $i18n.export_csv_neurons.project,
},
{
id: "symbol",
label: $i18n.export_csv_neurons.symbol,
},
{
id: "neuronAccountId",
label: $i18n.export_csv_neurons.neuron_account_id,
},
{
id: "controllerId",
label: $i18n.export_csv_neurons.controller_id,
},
{
id: "stake",
label: $i18n.export_csv_neurons.stake,
},
{
id: "availableMaturity",
label: $i18n.export_csv_neurons.available_maturity,
},
{
id: "stakedMaturity",
label: $i18n.export_csv_neurons.staked_maturity,
},
{
id: "dissolveDelaySeconds",
label: $i18n.export_csv_neurons.dissolve_delay,
},
{
id: "dissolveDate",
label: $i18n.export_csv_neurons.dissolve_date,
},
{
id: "creationDate",
label: $i18n.export_csv_neurons.creation_date,
},
{
id: "state",
label: $i18n.export_csv_neurons.state,
},
],
fileName,
});
} catch (error) {
console.error("Error exporting neurons:", error);

if (error instanceof FileSystemAccessError) {
toastsError({
labelKey: "export_error.file_system_access",
});
} else if (error instanceof CsvGenerationError) {
toastsError({
labelKey: "export_error.csv_generation",
});
} else {
toastsError({
labelKey: "export_error.neurons",
});
}
} finally {
dispatcher("nnsExportNeuronsCsvTriggered");
}
};
</script>

<button
data-tid="export-neurons-button-component"
on:click={exportNeurons}
class="text"
disabled={isDisabled}
aria-label={$i18n.header.export_neurons}
>
<IconDown />
{$i18n.header.export_neurons}
</button>

<style lang="scss">
@use "../../themes/mixins/account-menu";

button {
@include account-menu.button;
padding: 0;
}
</style>
27 changes: 25 additions & 2 deletions frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"send_with_token": "Send $token",
"collapse_all": "Collapse All",
"or": "or",
"add": "Add"
"add": "Add",
"not_applicable": "N/A"
},
"error": {
"auth_sync": "There was an unexpected issue while syncing the status of your authentication. Try to refresh your browser.",
Expand Down Expand Up @@ -150,7 +151,29 @@
"logout": "Logout",
"account_menu": "Open menu to access logout button",
"main_icp_account_id": "Main ICP Account ID",
"account_id_tooltip": "You can send ICP both to your principal ID and account ID, however some exchanges or wallets may not support transactions using a principal ID."
"account_id_tooltip": "You can send ICP both to your principal ID and account ID, however some exchanges or wallets may not support transactions using a principal ID.",
"export_neurons": "Export Neurons Info"
},
"export_csv_neurons": {
"account_id_label": "NNS Account Principal ID",
"date_label": "Export Date Time",
"controller_id": "Controller Principal ID",
"neuron_id": "Neuron ID",
"project": "Project",
"symbol": "Symbol",
"neuron_account_id": "Neuron Account ID",
"stake": "Stake",
"available_maturity": "Available Maturity",
"staked_maturity": "Staked Maturity",
"dissolve_delay": "Dissolve Delay",
"dissolve_date": "Dissolve Date",
"creation_date": "Creation Date",
"state": "State"
},
"export_error": {
"csv_generation": "Failed to generate CSV file",
"file_system_access": "Unable to save file. Please check your permissions.",
"neurons": "There was an error exporting the neurons. Please try again."
},
"auth": {
"login": "Sign in with Internet Identity",
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface I18nCore {
collapse_all: string;
or: string;
add: string;
not_applicable: string;
}

interface I18nError {
Expand Down Expand Up @@ -158,6 +159,30 @@ interface I18nHeader {
account_menu: string;
main_icp_account_id: string;
account_id_tooltip: string;
export_neurons: string;
}

interface I18nExport_csv_neurons {
account_id_label: string;
date_label: string;
controller_id: string;
neuron_id: string;
project: string;
symbol: string;
neuron_account_id: string;
stake: string;
available_maturity: string;
staked_maturity: string;
dissolve_delay: string;
dissolve_date: string;
creation_date: string;
state: string;
}

interface I18nExport_error {
csv_generation: string;
file_system_access: string;
neurons: string;
}

interface I18nAuth {
Expand Down Expand Up @@ -1400,6 +1425,8 @@ interface I18n {
warning: I18nWarning;
navigation: I18nNavigation;
header: I18nHeader;
export_csv_neurons: I18nExport_csv_neurons;
export_error: I18nExport_error;
auth: I18nAuth;
accounts: I18nAccounts;
neuron_types: I18nNeuron_types;
Expand Down
Loading