Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions site/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import DestroyedAssets from "./pages/destroyed/assets";
import NftClass from "./pages/nftClass";
import NftInstance from "./pages/nftInstance";
import DestroyedNfts from "./pages/destroyed/nfts";
import Runtimes from "./pages/runtimes";
import Runtime from "./pages/runtime";

function App() {
const { assets, uniques } = getChainModules();
Expand Down Expand Up @@ -59,6 +61,9 @@ function App() {
<Route path="/accounts/:id" element={<Account />} />
<Route path="/calls" element={<Calls />} />
<Route path="/calls/:id" element={<Call />} />
<Route path="/runtimes" element={<Runtimes />} />
{/* /runtimes/:version_:startHeight */}
<Route path="/runtimes/:runtimeSlug" element={<Runtime />} />
</Routes>
</HashRouter>
);
Expand Down
33 changes: 33 additions & 0 deletions site/src/components/runtime/runtimesTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import styled from "styled-components";
import { text_capitalize } from "../../styles/tailwindcss";
import { runtimesHead } from "../../utils/constants";
import { ColoredLink } from "../styled/link";
import Table from "../table";

const SpecName = styled.span`
${text_capitalize};
`;

/**
* @description page runtimes table
*/
export default function RuntimesTable({ data = [], loading }) {
const tableData = data?.map?.((item) => {
return [
<ColoredLink to={`/runtimes/${item.specVersion}-${item.startHeight}`}>
{item.specVersion}
</ColoredLink>,
<SpecName>{item.specName}</SpecName>,
<ColoredLink to={`/blocks/${item.startHeight}`}>
{item.startHeight?.toLocaleString?.()}
</ColoredLink>,
item.count.pallets,
item.count.calls,
item.count.events,
item.count.storage,
item.count.constants,
];
});

return <Table heads={runtimesHead} data={tableData} loading={loading} />;
}
60 changes: 60 additions & 0 deletions site/src/pages/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import BreadCrumb from "../components/breadCrumb";
import DetailLayout from "../components/layout/detailLayout";
import List from "../components/list";
import { Panel } from "../components/styled/panel";
import {
clearRuntimeDetail,
runtimeDetailSelector,
runtimeFetchDetail,
} from "../store/reducers/runtimeSlice";
import { bigNumberToLocaleString } from "../utils/viewFuncs";
import {
clearHttpError,
handleApiError,
} from "../utils/viewFuncs/errorHandles";
import { toRuntimeDetailItem } from "../utils/viewFuncs/toDetailItem";

export default function Runtime() {
const { runtimeSlug } = useParams();
const [version, startHeight] = runtimeSlug.split("-");

const dispatch = useDispatch();
const runtime = useSelector(runtimeDetailSelector);

const listData = runtime ? toRuntimeDetailItem(runtime) : {};

useEffect(() => {
if (version && startHeight) {
clearHttpError(dispatch);
dispatch(runtimeFetchDetail(version, startHeight)).catch((e) =>
handleApiError(e, dispatch),
);
}

return () => {
dispatch(clearRuntimeDetail());
};
}, [version, dispatch, startHeight]);

const breadCrumb = (
<BreadCrumb
data={[
{ name: "Runtimes", path: "/runtimes" },
{
name: version + "-" + bigNumberToLocaleString(startHeight),
},
]}
/>
);

return (
<DetailLayout breadCrumb={breadCrumb}>
<Panel>
<List data={listData} />
</Panel>
</DetailLayout>
);
}
62 changes: 62 additions & 0 deletions site/src/pages/runtimes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useLocation } from "react-router-dom";
import BreadCrumb from "../components/breadCrumb";
import Layout from "../components/layout";
import Pagination from "../components/pagination";
import RuntimesTable from "../components/runtime/runtimesTable";
import { StyledPanelTableWrapper } from "../components/styled/panel";
import {
clearRuntimeList,
runtimeFetchList,
runtimeListLoadingSelector,
runtimeListSelector,
} from "../store/reducers/runtimeSlice";
import { LIST_DEFAULT_PAGE_SIZE } from "../utils/constants";
import { getPageFromQuery } from "../utils/viewFuncs";

export default function Runtimes() {
const dispatch = useDispatch();
const location = useLocation();
const page = getPageFromQuery(location);
const pageSize = LIST_DEFAULT_PAGE_SIZE;

const list = useSelector(runtimeListSelector);
const loading = useSelector(runtimeListLoadingSelector);

useEffect(() => {
const controller = new AbortController();

dispatch(
runtimeFetchList(page - 1, pageSize, null, { signal: controller.signal }),
);

return () => {
controller.abort();
};
}, [dispatch, page, pageSize]);

useEffect(() => {
return () => {
dispatch(clearRuntimeList());
};
}, [dispatch]);

return (
<Layout>
<BreadCrumb data={[{ name: "Runtimes" }]} />

<StyledPanelTableWrapper
footer={
<Pagination
page={parseInt(page)}
pageSize={pageSize}
total={list?.total}
/>
}
>
<RuntimesTable data={list?.items} loading={loading} />
</StyledPanelTableWrapper>
</Layout>
);
}
5 changes: 5 additions & 0 deletions site/src/services/urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export const nftPopularListApi = `/uniques/classes/popular`;
export const nftInstanceApi = (classId, instanceId) =>
`/uniques/classes/${classId}/instances/${instanceId}`;

// runtimes
export const runtimeListApi = "/runtimes";
export const runtimeDetailApi = (version, startHeight) =>
`/runtimes/${version}_${startHeight}`;

// dotreasury
const dotreasuryApiEndPoint =
process.env.REACT_APP_PUBLIC_DOTREASURY_API_END_POINT ||
Expand Down
2 changes: 2 additions & 0 deletions site/src/store/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import assetReducer from "./assetSlice";
import nftReducer from "./nftSlice";
import nftClassReducer from "./nftClassSlice";
import nftInstanceReducer from "./nftInstanceSlice";
import runtimeReducer from "./runtimeSlice";

export default combineReducers({
setting: settingReducer,
Expand All @@ -38,4 +39,5 @@ export default combineReducers({
nft: nftReducer,
nftClass: nftClassReducer,
nftInstance: nftInstanceReducer,
runtime: runtimeReducer,
});
67 changes: 67 additions & 0 deletions site/src/store/reducers/runtimeSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { createSlice } from "@reduxjs/toolkit";
import api from "../../services/api";
import { runtimeDetailApi, runtimeListApi } from "../../services/urls";

const name = "runtime";

const runtimeSlice = createSlice({
name,
initialState: {
list: null,
listLoading: false,
detail: null,
},
reducers: {
setList(state, { payload }) {
state.list = payload;
},
setListLoading(state, { payload }) {
state.listLoading = payload;
},
setDetail(state, { payload }) {
state.detail = payload;
},
},
});

export const { setList, setListLoading, setDetail } = runtimeSlice.actions;

export const runtimeListSelector = (state) => state[name].list;
export const runtimeListLoadingSelector = (state) => state[name].listLoading;
export const runtimeDetailSelector = (state) => state[name].detail;

export const runtimeFetchList =
(page, pageSize, params, fetchOptions) => async (dispatch) => {
dispatch(setListLoading(true));

return api
.fetch(runtimeListApi, { page, pageSize, ...params }, fetchOptions)
.then(({ result }) => {
if (result) {
dispatch(setList(result));
}
})
.finally(() => {
dispatch(setListLoading(false));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here I think we should have mounted check.

});
};

export const clearRuntimeList = () => (dispatch) => {
dispatch(setList(null));
};

export const runtimeFetchDetail = (version, startHeight) => (dispatch) => {
return api
.fetch(runtimeDetailApi(version, startHeight))
.then(({ result }) => {
if (result) {
dispatch(setDetail(result));
}
});
};

export const clearRuntimeDetail = () => (dispatch) => {
dispatch(setDetail(null));
};

export default runtimeSlice.reducer;
5 changes: 5 additions & 0 deletions site/src/styles/tailwindcss.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ export const whitespace_nowrap = `
white-space: nowrap;
`;

// text transform
export const text_capitalize = `
text-transform: capitalize;
`;

// text overflow
export const text_ellipsis = `
text-overflow: ellipsis;
Expand Down
15 changes: 15 additions & 0 deletions site/src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export const menusBlockchain = [
name: "Calls",
value: "calls",
},
{
name: "Runtimes",
value: "runtimes",
},
{
type: "divider",
},
Expand Down Expand Up @@ -170,6 +174,17 @@ export const callsHead = [
},
];

export const runtimesHead = [
{ name: "VERSION", width: 120 },
{ name: "NAME", width: 232 },
{ name: "START HEIGHT", width: 160 },
{ name: "PALLETS", width: 160, align: "right" },
{ name: "CALLS", width: 160, align: "right" },
{ name: "EVENTS", width: 160, align: "right" },
{ name: "STORAGES", width: 160, align: "right" },
{ name: "CONSTANTS", width: 160, align: "right" },
];

export const transfersHead = [
{ name: "Event ID", width: 160 },
{ name: "Extrinsic ID", width: 160, align: "center" },
Expand Down
22 changes: 22 additions & 0 deletions site/src/utils/viewFuncs/toDetailItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { withCopy } from "../../HOC/withCopy";
import { TextSecondary } from "../../components/styled/text";
import {
ColoredInterLink,
ColoredInterLinkWithCopy,
ColoredMonoLink,
} from "../../components/styled/link";
import Tooltip from "../../components/tooltip";
Expand All @@ -29,6 +30,7 @@ import { bigNumberToLocaleString } from ".";
import { time } from "./time";
import { isCid } from "../cid";
import { getNftInstanceParsedMetadata } from "../nft";
import capitalize from "lodash.capitalize";

const TextSecondaryWithCopy = withCopy(TextSecondary);
const ColoredMonoLinkWithCopy = withCopy(ColoredMonoLink);
Expand Down Expand Up @@ -237,6 +239,26 @@ export const toCallDetailItem = (indexer, section, method) => {
};
};

export const toRuntimeDetailItem = (runtime) => {
return {
Version: (
<TextSecondary>{runtime?.runtimeVersion?.specVersion}</TextSecondary>
),
ID: <TextSecondary>-</TextSecondary>,
"Start Height": (
<ColoredInterLinkWithCopy to={`/blocks/${runtime?.height}`}>
{runtime?.height?.toLocaleString?.()}
</ColoredInterLinkWithCopy>
),
"Spec Name": (
<TextSecondary>
{capitalize(runtime?.runtimeVersion?.specName)}
</TextSecondary>
),
Pallets: <TextSecondary>-</TextSecondary>,
};
};

export const toEventDetailItem = (event) => {
return {
"Event Time": <DetailedTime ts={event?.indexer?.blockTime} />,
Expand Down