Skip to content
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import ErrorMessage from './components/error/ErrorMessage';
import WebSocketHandler from './WebSocketHandler';
import NanoContractDetail from './screens/nano/NanoContractDetail';
import BlueprintDetail from './screens/nano/BlueprintDetail';
import BlueprintList from './screens/nano/BlueprintList';
import {
apiLoadErrorUpdate,
dashboardUpdate,
Expand Down Expand Up @@ -167,6 +168,7 @@ function Root() {
path="/blueprint/detail/:blueprint_id"
element={<NavigationRoute internalScreen={BlueprintDetail} />}
/>
<Route path="/blueprints/" element={<NavigationRoute internalScreen={BlueprintList} />} />
<Route path="" element={<NavigationRoute internalScreen={DashboardTx} />} />
</Routes>
</BrowserRouter>
Expand Down
72 changes: 72 additions & 0 deletions src/api/nanoApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,78 @@ const nanoApi = {
);
});
},

/**
* Get built in blueprint list
*
* @param {number | null} count Number of elements to get the list
* @param {string | null} after ID of the blueprint to get as reference for after pagination
* @param {string | null} before ID of the blueprint to get as reference for before pagination
* @param {string | null} search Blueprint ID to search for in the API
*
* For more details, see full node api docs
*/
getBuiltInBlueprintList(count, after, before, search) {
const data = {};
if (count) {
data.count = count;
}
if (after) {
data.after = after;
}
if (before) {
data.before = before;
}
if (search) {
data.search = search;
}
return requestExplorerServiceV1
.get(`node_api/nc_builtin_blueprints`, { params: data })
.then(res => res.data)
.catch(err => {
throw new Error(
err?.data?.message || err?.message || `Unknown error on get builtin blueprint list`
);
});
},

/**
* Get on chain blueprint list
*
* @param {number | null} count Number of elements to get the list
* @param {string | null} after ID of the blueprint to get as reference for after pagination
* @param {string | null} before ID of the blueprint to get as reference for before pagination
* @param {string | null} search Blueprint ID to search for in the API
* @param {string | null} order Order of the sorting ('asc' or 'desc')
*
* For more details, see full node api docs
*/
getOnChainBlueprintList(count, after, before, search, order) {
const data = {};
if (count) {
data.count = count;
}
if (after) {
data.after = after;
}
if (before) {
data.before = before;
}
if (search) {
data.search = search;
}
if (order) {
data.order = order;
}
return requestExplorerServiceV1
.get(`node_api/nc_on_chain_blueprints`, { params: data })
.then(res => res.data)
.catch(err => {
throw new Error(
err?.data?.message || err?.message || `Unknown error on get on chain blueprint list`
);
});
},
};

export default nanoApi;
5 changes: 4 additions & 1 deletion src/components/AddressHistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import EllipsiCell from './EllipsiCell';
import { ReactComponent as RowBottomIcon } from '../assets/images/leading-icon.svg';
import { ReactComponent as RowTopIcon } from '../assets/images/leading-top-icon.svg';
import { COLORS } from '../constants';
import { useIsMobile } from '../hooks';

const mapStateToProps = state => ({
decimalPlaces: state.serverInfo.decimal_places,
Expand Down Expand Up @@ -174,6 +175,8 @@ class AddressHistory extends SortableTable {
}

renderNewTableBodyUi() {
const isMobile = useIsMobile();
const ellipsisCount = isMobile ? 4 : 12;
return this.props.data.map(tx => {
let statusElement = '';
let trClass = '';
Expand Down Expand Up @@ -224,7 +227,7 @@ class AddressHistory extends SortableTable {
<tr key={tx.tx_id} className={trClass} onClick={_e => this.props.onRowClicked(tx.tx_id)}>
<td className="pe-3">{hathorLib.transactionUtils.getTxType(tx)}</td>
<td className="pe-3">
<EllipsiCell id={tx.tx_id} />
<EllipsiCell id={tx.tx_id} countBefore={ellipsisCount} countAfter={ellipsisCount} />
</td>
<td className="pe-3 td-mobile date-cell">
{dateFormatter.parseTimestampNewUi(tx.timestamp)}
Expand Down
8 changes: 3 additions & 5 deletions src/components/EllipsiCell.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
*/

import React from 'react';
import { useIsMobile } from '../hooks';

const EllipsiCell = ({ id }) => {
const isMobile = useIsMobile();
const idStart = id.substring(0, isMobile ? 4 : 12);
const idEnd = id.substring(id.length - (isMobile ? 4 : 12), id.length);
const EllipsiCell = ({ id, countBefore, countAfter }) => {
const idStart = id.substring(0, countBefore);
const idEnd = id.substring(id.length - countAfter, id.length);

return (
<div className="id-cell">
Expand Down
18 changes: 18 additions & 0 deletions src/components/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ function Navigation() {
</div>
</ul>
)}
<li className="nav-item dropdown">
<span
className="nav-link dropdown-toggle custom-dropdown-toggle"
id="navbarDropdown"
role="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Nano
<ArrorDownNavItem className="dropdown-icon" />
</span>
<div className="dropdown-menu" aria-labelledby="navbarDropdown">
<NavLink to="/blueprints/?type=built-in" exact className="nav-link">
Blueprints List
</NavLink>
</div>
</li>
<li className="nav-item">
<NavLink
to="/network"
Expand Down
21 changes: 21 additions & 0 deletions src/components/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function Sidebar({ close, open }) {
const theme = useSelector(state => state.theme);
const sidebarRef = useRef(null);
const [tokensOpen, setTokensOpen] = useState(false);
const [nanoOpen, setNanoOpen] = useState(false);
const [toolsOpen, setToolsOpen] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -95,6 +96,26 @@ function Sidebar({ close, open }) {
)}
</span>
)}
<li className="nav-item item-sidebar">
<span onClick={() => setNanoOpen(!nanoOpen)}>
Nano{' '}
<ArrorDownNavItem
style={{ marginLeft: '5px', rotate: toolsOpen ? '180deg' : '0deg' }}
className="dropdown-icon"
/>
</span>
{nanoOpen && (
<div>
<ul style={{ listStyleType: 'none', padding: 0, margin: 0 }}>
<li>
<NavLink to="/blueprints/?type=built-in" exact className="nav-link">
Blueprints List
</NavLink>
</li>
</ul>
</div>
)}
</li>
<li className="nav-item item-sidebar">
<NavLink
to="/network"
Expand Down
11 changes: 8 additions & 3 deletions src/components/SortableTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ class SortableTable extends React.Component {

renderTable(content) {
return this.props.newUiEnabled ? (
<table className="table-stylized table-tokens" id="">
<table
className={`table-stylized ${
this.props.tableClass ? this.props.tableClass : 'table-tokens'
}`}
>
{content}
</table>
) : (
Expand Down Expand Up @@ -150,7 +154,7 @@ class SortableTable extends React.Component {
if (this.props.calculatingPage) {
return 'page-item disable-button';
}
return 'page-item active';
return 'page-item';
Comment on lines -153 to +157
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ana decided to remove the active style from table pagination buttons, so both pagination buttons have the same style now.

})()}
>
<button onClick={e => this.props.onNextPageClicked(e)} className="page-link">
Expand Down Expand Up @@ -196,7 +200,7 @@ class SortableTable extends React.Component {
* order: If sorted field must be ordered asc or desc
* tableHeaderClicked: This indicates that user wants data to be sorted by a determined field
* calculatingPage: Indicates if next page is being retrieved from explorer-service
* tableClasses: Extra classes to add to the table element
* tableClass: CSS class to add to the table element. The default value is 'table-tokens'.
*/
SortableTable.propTypes = {
data: PropTypes.array.isRequired,
Expand All @@ -209,6 +213,7 @@ SortableTable.propTypes = {
tableHeaderClicked: PropTypes.func,
sortBy: PropTypes.string,
order: PropTypes.string,
tableClass: PropTypes.string,
};

export default SortableTable;
38 changes: 38 additions & 0 deletions src/components/nano/BuiltInBlueprintsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import SortableTable from '../SortableTable';
import EllipsiCell from '../EllipsiCell';

// XXX We should use function component with SortableTable as a component
// but renderTableHead and renderTableBody are implemented and not
// expected as a props, so it demands a bigger refactor
Comment on lines +5 to +7
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐ Thanks for highlighting this tech debt here! This is pending since before the UX revamp.

class BuiltInBlueprintsTable extends SortableTable {
renderTableHead() {
return (
<tr>
<th className="d-lg-table-cell">BLUEPRINT ID</th>
<th className="d-lg-table-cell">NAME</th>
</tr>
);
}

renderTableBody() {
return this.props.data.map(blueprint => {
return (
<tr key={blueprint.id} onClick={_e => this.props.handleClickRow(blueprint.id)}>
<td className="d-lg-table-cell pe-3">
{this.props.isMobile ? (
<EllipsiCell id={blueprint.id} countBefore={10} countAfter={10} />
) : (
blueprint.id
)}
</td>
<td className="d-lg-table-cell pe-3">{blueprint.name}</td>
</tr>
);
});
}
}

BuiltInBlueprintsTable.propTypes = SortableTable.propTypes;

export default BuiltInBlueprintsTable;
76 changes: 76 additions & 0 deletions src/components/nano/OnChainBlueprintsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import SortableTable from '../SortableTable';
import EllipsiCell from '../EllipsiCell';
import dateFormatter from '../../utils/date';

// XXX We should use function component with SortableTable as a component
// but renderTableHead and renderTableBody are implemented and not
// expected as a props, so it demands a bigger refactor
class OnChainBlueprintsTable extends SortableTable {
/*
* Get header of first column. In the mobile we show the element
* of two columns in one.
*/
getFirstHeaderName() {
if (this.props.isMobile) {
return 'BLUEPRINT ID AND NAME';
}

return 'BLUEPRINT ID';
}

renderTableHead() {
return (
<tr>
<th className="d-lg-table-cell">{this.getFirstHeaderName()}</th>
{!this.props.isMobile && <th className="d-lg-table-cell">NAME</th>}
<th
className="d-lg-table-cell sortable"
onClick={e => this.props.tableHeaderClicked(e, 'created_at')}
>
CREATED AT {this.getArrow('created_at')}
</th>
</tr>
);
}

renderMobileRow(blueprint) {
return (
<>
<td className="d-lg-table-cell pe-3">
<EllipsiCell id={blueprint.id} countBefore={10} countAfter={10} />
<p className="mt-2 mb-0">{blueprint.name}</p>
</td>
<td className="d-lg-table-cell pe-3">
{dateFormatter.parseTimestampNewUi(blueprint.created_at)}
</td>
</>
);
}

renderDesktopRow(blueprint) {
return (
<>
<td className="d-lg-table-cell pe-3">{blueprint.id}</td>
<td className="d-lg-table-cell pe-3">{blueprint.name}</td>
<td className="d-lg-table-cell pe-3">
{dateFormatter.parseTimestampNewUi(blueprint.created_at)}
</td>
</>
);
}

renderTableBody() {
return this.props.data.map(blueprint => {
return (
<tr key={blueprint.id} onClick={_e => this.props.handleClickRow(blueprint.id)}>
{this.props.isMobile ? this.renderMobileRow(blueprint) : this.renderDesktopRow(blueprint)}
</tr>
);
});
}
}

OnChainBlueprintsTable.propTypes = SortableTable.propTypes;

export default OnChainBlueprintsTable;
4 changes: 3 additions & 1 deletion src/components/token/TokenBalanceRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ function TokenBalanceRow({ tokenId, address, total, unlocked, locked }) {

const renderNewUi = () => (
<tr onClick={onRowClicked}>
<td className="d-lg-table-cell pe-3">{isMobile ? <EllipsiCell id={address} /> : address}</td>
<td className="d-lg-table-cell pe-3">
{isMobile ? <EllipsiCell id={address} countBefore={4} countAfter={4} /> : address}
</td>
<td className="d-lg-table-cell pe-3">{numberUtils.prettyValue(total, decimalPlaces)}</td>
<td className="d-lg-table-cell pe-3 td-mobile">
{numberUtils.prettyValue(unlocked, decimalPlaces)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/token/TokenRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ function TokenRow({ token }) {
isMobile ? (
<tr onClick={_e => onRowClicked(token.uid)}>
<td className="d-lg-table-cell pe-3">
<EllipsiCell id={token.uid} />
<EllipsiCell id={token.uid} countBefore={4} countAfter={4} />
</td>
<td className="d-lg-table-cell pe-3">{token.name}</td>
</tr>
) : (
<tr onClick={_e => onRowClicked(token.uid)}>
<td className="d-lg-table-cell pe-3">
<EllipsiCell id={token.uid} />
<EllipsiCell id={token.uid} countBefore={12} countAfter={12} />
</td>
<td className="d-lg-table-cell pe-3">{token.name}</td>
<td className="d-lg-table-cell pe-3">
Expand Down
2 changes: 1 addition & 1 deletion src/components/token/TokensTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class TokensTable extends SortableTable {
className="d-lg-table-cell sortable th-table-token-mobile"
onClick={e => this.props.tableHeaderClicked(e, 'transaction_timestamp')}
>
Created At
Created At {this.getArrow('transaction_timestamp')}
</th>
</tr>
) : (
Expand Down
Loading