Skip to content

Commit ecca749

Browse files
authored
Merge pull request #83 from iotaledger/feat/base-token-support
Feat: Add base token and display native tokens on Address page
2 parents 8d12a6d + 3eea31d commit ecca749

File tree

12 files changed

+316
-64
lines changed

12 files changed

+316
-64
lines changed

src/app/components/tangle/MigratedFund.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { ED25519_ADDRESS_TYPE, UnitsHelper } from "@iota/iota.js";
1+
import { ED25519_ADDRESS_TYPE } from "@iota/iota.js";
22
import classNames from "classnames";
33
import React, { Component, ReactNode } from "react";
44
import { ServiceFactory } from "../../../factories/serviceFactory";
55
import { NodeConfigService } from "../../../services/nodeConfigService";
66
import { Bech32AddressHelper } from "../../../utils/bech32AddressHelper";
7+
import { FormatHelper } from "../../../utils/formatHelper";
78
import { ReactComponent as DropdownIcon } from "./../../../assets/dropdown-arrow.svg";
89
import Bech32Address from "./Bech32Address";
910
import { MigratedFundProps } from "./MigratedFundProps";
@@ -90,9 +91,10 @@ class MigratedFund extends Component<MigratedFundProps, MigratedFundState> {
9091
}
9192
)}
9293
>
93-
{this.state.formatFull
94-
? `${this.props.fund.deposit} i`
95-
: UnitsHelper.formatBest(Number(this.props.fund.deposit))}
94+
{FormatHelper.getInstance().amount(
95+
Number(this.props.fund.deposit),
96+
this.state.formatFull
97+
)}
9698
</button>
9799
</div>
98100
</div>

src/app/components/tangle/Output.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { BASIC_OUTPUT_TYPE, ALIAS_OUTPUT_TYPE, FOUNDRY_OUTPUT_TYPE, NFT_OUTPUT_TYPE, TREASURY_OUTPUT_TYPE, UnitsHelper, IOutputResponse, SIMPLE_TOKEN_SCHEME_TYPE } from "@iota/iota.js";
1+
import { BASIC_OUTPUT_TYPE, ALIAS_OUTPUT_TYPE, FOUNDRY_OUTPUT_TYPE, NFT_OUTPUT_TYPE, TREASURY_OUTPUT_TYPE, IOutputResponse, SIMPLE_TOKEN_SCHEME_TYPE } from "@iota/iota.js";
22
import classNames from "classnames";
33
import React, { Component, ReactNode } from "react";
44
import { Link } from "react-router-dom";
55
import { ClipboardHelper } from "../../../utils/clipboardHelper";
6+
import { FormatHelper } from "../../../utils/formatHelper";
67
import { NameHelper } from "../../../utils/nameHelper";
78
import MessageButton from "../layout/MessageButton";
89
import { ReactComponent as DropdownIcon } from "./../../../assets/dropdown-arrow.svg";
@@ -65,9 +66,10 @@ class Output extends Component<OutputProps, OutputState> {
6566
}
6667
)}
6768
>
68-
{this.state.formatFull
69-
? `${this.state.output.amount} i`
70-
: UnitsHelper.formatBest(Number(this.state.output.amount))}
69+
{FormatHelper.getInstance().amount(
70+
Number(this.state.output.amount),
71+
this.state.formatFull
72+
)}
7173
</button>
7274
</div>
7375
</div>

src/app/components/tangle/ReceiptMilestoneOption.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { UnitsHelper } from "@iota/iota.js";
21
import classNames from "classnames";
32
import React, { Component, ReactNode } from "react";
3+
import { FormatHelper } from "../../../utils/formatHelper";
44
import { ReactComponent as DropdownIcon } from "./../../../assets/dropdown-arrow.svg";
55
import MigratedFund from "./MigratedFund";
66
import { ReceiptMilestoneOptionProps } from "./ReceiptMilestoneOptionProps";
@@ -83,9 +83,10 @@ class ReceiptMilestoneOption extends Component<ReceiptMilestoneOptionProps, Rece
8383
}
8484
)}
8585
>
86-
{this.state.formatFull
87-
? `${this.props.option.transaction.output.amount} i`
88-
: UnitsHelper.formatBest(Number(this.props.option.transaction.output.amount))}
86+
{FormatHelper.getInstance().amount(
87+
Number(this.props.option.transaction.output.amount),
88+
this.state.formatFull
89+
)}
8990
</button>
9091
</div>
9192
</div>

src/app/routes/explorer/Address.tsx

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
import { IOutputResponse, UnitsHelper } from "@iota/iota.js";
1+
import { IOutputResponse } from "@iota/iota.js";
2+
import classNames from "classnames";
23
import React, { ReactNode } from "react";
34
import { Link, RouteComponentProps } from "react-router-dom";
45
import { ReactComponent as ChevronLeftIcon } from "../../../assets/chevron-left.svg";
56
import { ServiceFactory } from "../../../factories/serviceFactory";
67
import { NodeConfigService } from "../../../services/nodeConfigService";
78
import { TangleService } from "../../../services/tangleService";
89
import { Bech32AddressHelper } from "../../../utils/bech32AddressHelper";
10+
import { FormatHelper } from "../../../utils/formatHelper";
911
import AsyncComponent from "../../components/layout/AsyncComponent";
1012
import Spinner from "../../components/layout/Spinner";
1113
import Bech32Address from "../../components/tangle/Bech32Address";
1214
import Output from "../../components/tangle/Output";
15+
import { ReactComponent as DropdownIcon } from "./../../../assets/dropdown-arrow.svg";
1316
import "./Address.scss";
1417
import { AddressRouteProps } from "./AddressRouteProps";
1518
import { AddressState } from "./AddressState";
@@ -44,6 +47,7 @@ class Address extends AsyncComponent<RouteComponentProps<AddressRouteProps>, Add
4447
...Bech32AddressHelper.buildAddress(props.match.params.address, this._bech32Hrp),
4548
formatFull: false,
4649
statusBusy: true,
50+
showTokens: false,
4751
status: "Loading outputs..."
4852
};
4953
}
@@ -113,43 +117,83 @@ class Address extends AsyncComponent<RouteComponentProps<AddressRouteProps>, Add
113117
<h3 className="secondary margin-l-s">Back to Explorer</h3>
114118
</Link>
115119
<div className="card margin-t-m padding-l">
116-
<h2>Address</h2>
117-
<Bech32Address
118-
activeLinks={false}
119-
addressDetails={this.state.bech32AddressDetails}
120-
/>
121-
{this.state.balance !== undefined && (
122-
<div>
123-
<div className="card--label">
124-
Balance
120+
<div className="card--content padding-0">
121+
<h2>Address</h2>
122+
<Bech32Address
123+
activeLinks={false}
124+
addressDetails={this.state.bech32AddressDetails}
125+
/>
126+
{this.state.balance !== undefined && (
127+
<div>
128+
<div className="card--label">
129+
Balance
130+
</div>
131+
<div className="card--value card--value__mono">
132+
<button
133+
className="card--value--button"
134+
type="button"
135+
onClick={() => this.setState(
136+
{
137+
formatFull: !this.state.formatFull
138+
}
139+
)}
140+
>
141+
{FormatHelper.getInstance().amount(
142+
Number(this.state.balance),
143+
this.state.formatFull
144+
)}
145+
</button>
146+
</div>
147+
{this.state.address?.nativeTokens && (
148+
<React.Fragment>
149+
<div
150+
className="card--content__input margin-t-s"
151+
onClick={() => this.setState({ showTokens: !this.state.showTokens })}
152+
>
153+
<div className={classNames(
154+
"margin-r-t",
155+
"card--content__input--dropdown",
156+
{ "opened": this.state.showTokens }
157+
)}
158+
>
159+
<DropdownIcon />
160+
</div>
161+
<h3 className="card--content__input--label">
162+
Native Tokens
163+
</h3>
164+
</div>
165+
{this.state.showTokens &&
166+
Object.keys(this.state.address?.nativeTokens).map((key, idx) => (
167+
<div className="card--content--border-l" key={idx}>
168+
<div className="card--label">
169+
Token Id
170+
</div>
171+
<div className="card--value card--value__mono">
172+
{key}
173+
</div>
174+
<div className="card--label">
175+
Amount
176+
</div>
177+
<div className="card--value card--value__mono">
178+
{this.state.address?.nativeTokens[key].toString()}
179+
</div>
180+
</div>
181+
))}
182+
</React.Fragment>
183+
)}
125184
</div>
126-
<div className="card--value card--value__mono">
127-
<button
128-
className="card--value--button"
129-
type="button"
130-
onClick={() => this.setState(
131-
{
132-
formatFull: !this.state.formatFull
133-
}
134-
)}
135-
>
136-
{this.state.formatFull
137-
? `${this.state.balance} i`
138-
: UnitsHelper.formatBest(Number(this.state.balance))}
139-
</button>
185+
)}
186+
{this.state.status && (
187+
<div className="middle row margin-t-m">
188+
{this.state.statusBusy && (<Spinner compact={true} />)}
189+
<p className="status margin-l-s">
190+
{this.props.match.params.address}
191+
<br />
192+
{this.state.status}
193+
</p>
140194
</div>
141-
</div>
142-
)}
143-
{this.state.status && (
144-
<div className="middle row margin-t-m">
145-
{this.state.statusBusy && (<Spinner compact={true} />)}
146-
<p className="status margin-l-s">
147-
{this.props.match.params.address}
148-
<br />
149-
{this.state.status}
150-
</p>
151-
</div>
152-
)}
195+
)}
196+
</div>
153197
</div>
154198

155199
{this.state.outputs &&

src/app/routes/explorer/AddressState.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ export interface AddressState {
4242
* Format the amount in full.
4343
*/
4444
formatFull: boolean;
45+
46+
/**
47+
* Show native tokens.
48+
*/
49+
showTokens: boolean;
4550
}

src/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { EventAggregator } from "./services/eventAggregator";
1212
import { LocalStorageService } from "./services/localStorageService";
1313
import { MetricsService } from "./services/metricsService";
1414
import { NodeConfigService } from "./services/nodeConfigService";
15+
import { SessionStorageService } from "./services/sessionStorageService";
1516
import { SettingsService } from "./services/settingsService";
1617
import { TangleService } from "./services/tangleService";
1718
import { ThemeService } from "./services/themeService";
@@ -39,7 +40,8 @@ initServices()
3940
* @returns The brand configuration.
4041
*/
4142
async function initServices(): Promise<IBrandConfiguration | undefined> {
42-
ServiceFactory.register("storage", () => new LocalStorageService());
43+
ServiceFactory.register("local-storage", () => new LocalStorageService());
44+
ServiceFactory.register("session-storage", () => new SessionStorageService());
4345
const settingsService = new SettingsService();
4446
ServiceFactory.register("settings", () => settingsService);
4547

src/services/authService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class AuthService {
4141
* Initialise service.
4242
*/
4343
public async initialize(): Promise<void> {
44-
const storageService = ServiceFactory.get<LocalStorageService>("storage");
44+
const storageService = ServiceFactory.get<LocalStorageService>("local-storage");
4545

4646
const jwt = storageService.load<string>("dashboard-jwt");
4747

@@ -87,7 +87,7 @@ export class AuthService {
8787
headers);
8888

8989
if (response.jwt) {
90-
const storageService = ServiceFactory.get<LocalStorageService>("storage");
90+
const storageService = ServiceFactory.get<LocalStorageService>("local-storage");
9191
this._jwt = response.jwt;
9292
storageService.save<string>("dashboard-jwt", this._jwt);
9393
EventAggregator.publish("auth-state", true);
@@ -104,7 +104,7 @@ export class AuthService {
104104
*/
105105
public logout(): void {
106106
if (this._jwt) {
107-
const storageService = ServiceFactory.get<LocalStorageService>("storage");
107+
const storageService = ServiceFactory.get<LocalStorageService>("local-storage");
108108
storageService.remove("dashboard-jwt");
109109
this._jwt = undefined;
110110
EventAggregator.publish("auth-state", false);

src/services/nodeConfigService.ts

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { INodeInfoBaseToken } from "@iota/iota.js";
12
import { ServiceFactory } from "../factories/serviceFactory";
3+
import { SessionStorageService } from "./sessionStorageService";
24
import { TangleService } from "./tangleService";
35

46
/**
@@ -8,32 +10,56 @@ export class NodeConfigService {
810
/**
911
* The network id.
1012
*/
11-
private _networkId?: string;
13+
private _networkId: string;
1214

1315
/**
1416
* The bech32 hrp.
1517
*/
16-
private _bech32Hrp?: string;
18+
private _bech32Hrp: string;
19+
20+
/**
21+
* The bech32 hrp.
22+
*/
23+
private _baseToken: INodeInfoBaseToken;
24+
25+
/**
26+
* The storage servie.
27+
*/
28+
private readonly _storageService: SessionStorageService;
1729

1830
/**
1931
* Create a new instance of NodeConfigService.
2032
*/
2133
constructor() {
22-
this._networkId = undefined;
23-
this._bech32Hrp = undefined;
34+
this._storageService = ServiceFactory.get<SessionStorageService>("session-storage");
35+
this._bech32Hrp = "iota";
36+
this._networkId = "";
37+
this._baseToken = {
38+
name: "IOTA",
39+
tickerSymbol: "MIOTA",
40+
unit: "i",
41+
decimals: 0,
42+
subunit: undefined,
43+
useMetricPrefix: true
44+
};
2445
}
2546

2647
/**
2748
* Initialise NodeConfigService.
2849
*/
2950
public async initialize(): Promise<void> {
30-
if (!this._bech32Hrp || !this._networkId) {
51+
this._bech32Hrp = this._storageService.load<string>("bech32Hrp");
52+
this._networkId = this._storageService.load<string>("networkId");
53+
this._baseToken = this._storageService.load<INodeInfoBaseToken>("baseToken");
54+
55+
if (!this._bech32Hrp || !this._networkId || !this._baseToken) {
3156
const tangleService = ServiceFactory.get<TangleService>("tangle");
3257

3358
try {
3459
const info = await tangleService.info();
35-
this._bech32Hrp = info.protocol.bech32HRP;
36-
this._networkId = info.protocol.networkName;
60+
this.setBech32Hrp(info.protocol.bech32HRP);
61+
this.setNetworkId(info.protocol.networkName);
62+
this.setBaseToken(info.baseToken);
3763
} catch {}
3864
}
3965
}
@@ -43,14 +69,49 @@ export class NodeConfigService {
4369
* @returns The bech32 hrp.
4470
*/
4571
public getBech32Hrp(): string {
46-
return this._bech32Hrp ?? "iota";
72+
return this._bech32Hrp;
4773
}
4874

4975
/**
5076
* Get the netwoork id.
5177
* @returns The network id.
5278
*/
5379
public getNetworkId(): string {
54-
return this._networkId ?? "";
80+
return this._networkId;
81+
}
82+
83+
/**
84+
* Get the node base token.
85+
* @returns The node base token.
86+
*/
87+
public getBaseToken(): INodeInfoBaseToken {
88+
return this._baseToken;
89+
}
90+
91+
/**
92+
* Set the hrp for bech32 addresses.
93+
* @param bech32Hrp The new blind mode.
94+
*/
95+
public setBech32Hrp(bech32Hrp: string): void {
96+
this._bech32Hrp = bech32Hrp;
97+
this._storageService.save<string>("bech32Hrp", this._bech32Hrp);
98+
}
99+
100+
/**
101+
* Set the network id.
102+
* @param networkId The new blind mode.
103+
*/
104+
public setNetworkId(networkId: string): void {
105+
this._networkId = networkId;
106+
this._storageService.save<string>("networkId", this._networkId);
107+
}
108+
109+
/**
110+
* Set the base token.
111+
* @param baseToken The new blind mode.
112+
*/
113+
public setBaseToken(baseToken: INodeInfoBaseToken): void {
114+
this._baseToken = baseToken;
115+
this._storageService.save("baseToken", this._baseToken);
55116
}
56117
}

0 commit comments

Comments
 (0)