Skip to content

Commit

Permalink
prepare for self-hosting
Browse files Browse the repository at this point in the history
  • Loading branch information
uiii committed Jan 22, 2024
1 parent 76954c8 commit 2106674
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 690 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ Calamar is a block explorer for Polkadot, Kusama and their parachains. The explo

This app is based on [Create React App](https://facebook.github.io/create-react-app/docs/getting-started).

> [!WARNING]
> Since January 31, Calamar and its data source can only be **self-hosted**.
> This is following the deprecation of Firesquid archives and its resulting effect on Giant Squid.
>
> Read more about [data sources](https://github.com/topmonks/calamar/wiki/Data-sources).
>
> *We are looking for a solution to make Calamar online and publicly available again, but we have no ETA yet.*
>
> We apologize for any inconvenience.
## Setup

Clone the repo and install the NPM dependencies.
Expand All @@ -14,6 +24,27 @@ $ cd calamar
$ npm install
```

Edit `src/networks.json` and configure your network and its [data source's](https://github.com/topmonks/calamar/wiki/Data-sources) endpoints.

<details>
<summary>Network configuration structure</summary>

- **name**: network identificator used in url etc. (required, `"polkadot"`)
- **displayName**: name of the network to be displayed in the app (required, `"Polkadot"`)
- **icon**: path to icon asset (required, e.g. `"/assets/network-icons/polkadot.svg"`)
- **color**: color associated with the network (optional, e.g. `"#e6007a"`)
- **prefix**: SS58 prefix (required, e.g. `0`)
- **decimals**: number of decimals for the network's symbol (required, e.g. `10`)
- **symbol**: network's symbol (required, `"DOT"`)
- **squids**:
- **archive**: GraphQL API explorer of the [Firesquid archive](https://github.com/topmonks/calamar/wiki/Data-sources#firesquid-archive) (required, e.g. `"https://polkadot.explorer.subsquid.io/graphql"`)
- **explorer**: [GiantSquid explorer](https://github.com/topmonks/calamar/wiki/Data-sources#giantsquid-explorer) (optional, but highly recommended, e.g. `"https://squid.subsquid.io/gs-explorer-polkadot/graphql"`)
- **main**: [GiantSquid main](https://github.com/topmonks/calamar/wiki/Data-sources#giantsquid-main) (optional, e.g. `"https://squid.subsquid.io/gs-main-polkadot/graphql"`)
- **identites**: [GiantSquid main](https://github.com/topmonks/calamar/wiki/Data-sources#giantsquid-main) if it provides indentity data (optional, e.g. `"https://squid.subsquid.io/gs-main-polkadot/graphql"`)
- **stats**: [GianSquid stats](https://github.com/topmonks/calamar/wiki/Data-sources#giantsquid-stats) (optional, e.g. `"https://squid.subsquid.io/gs-stats-polkadot/graphql"`)
- **coinGeckoId**: CoinGecko network ID to fetch USD values (optional, `"polkadot"`)
</details>

## Run locally

To start the application locally, run the command
Expand Down
159 changes: 59 additions & 100 deletions src/components/NetworkSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/** @jsxImportSource @emotion/react */
import { useCallback, useState } from "react";
import { Button, ButtonProps, Checkbox, Divider, ListItem, ListItemIcon, ListItemText, ListSubheader, Menu, MenuItem } from "@mui/material";
import { useCallback, useEffect, useState } from "react";
import { Button, ButtonProps, Checkbox, Divider, ListItemIcon, ListItemText, Menu, MenuItem } from "@mui/material";
import { grey } from "@mui/material/colors";
import { BlurOn as AllIcon, ArrowDropDown } from "@mui/icons-material";
import { Theme, css } from "@emotion/react";
import { css } from "@emotion/react";

import { useNetworkGroups } from "../hooks/useNetworkGroups";
import { Network } from "../model/network";

import { Link } from "./Link";
import { useNetworks } from "../hooks/useNetworks";

const buttonStyle = css`
display: flex;
Expand All @@ -28,22 +27,6 @@ const buttonStyle = css`
}
`;

const headerStyle = css`
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 12px;
padding-bottom: 20px;
line-height: 1.2;
a {
font-weight: 400;
cursor: pointer;
}
`;

const menuItemStyle = css`
.MuiListItemIcon-root {
min-width: 0px;
Expand Down Expand Up @@ -95,60 +78,52 @@ interface NetworkSelectProps extends Omit<ButtonProps, "value" | "onChange"> {
export const NetworkSelect = (props: NetworkSelectProps) => {
const { value, multiselect, onChange, ...buttonProps } = props;

const networkGroups = useNetworkGroups();
const networks = useNetworks();

const [anchorEl, setAnchorEl] = useState<HTMLElement>();

const open = !!anchorEl;
const handleClose = useCallback(() => setAnchorEl(undefined), []);

const setSelection = useCallback((networks: Network[]) => {
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);

const setSelection = useCallback((selected: Network[]) => {
const newValue = [];

for (const networkGroup of networkGroups) {
for (const network of networkGroup.networks) {
if (networks.includes(network)) {
newValue.push(network);
}
for (const network of networks) {
if (selected.includes(network)) {
newValue.push(network);
}
}

onChange?.(newValue, true);
handleClose();
}, [onChange]);
}, [networks, onChange]);

const addSelection = useCallback((networks: Network[]) => {
const addSelection = useCallback((selected: Network[]) => {
const newValue = [];

for (const networkGroup of networkGroups) {
for (const network of networkGroup.networks) {
if (networks.includes(network) || value.includes(network)) {
newValue.push(network);
}
for (const network of networks) {
if (selected.includes(network) || value.includes(network)) {
newValue.push(network);
}
}

onChange?.(newValue, true);
}, [value, networkGroups, onChange]);
}, [value, networks, onChange]);

const removeSelection = useCallback((networks: Network[]) => {
const removeSelection = useCallback((selected: Network[]) => {
const newValue = [];

for (const networkGroup of networkGroups) {
for (const network of networkGroup.networks) {
if (value.includes(network) && !networks.includes(network)) {
newValue.push(network);
}
for (const network of networks) {
if (value.includes(network) && !selected.includes(network)) {
newValue.push(network);
}
}

onChange?.(newValue, true);
}, [value, networkGroups, onChange]);

const handleClose = useCallback(() => setAnchorEl(undefined), []);

const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
}, [value, networks, onChange]);

const handleItemClick = useCallback((network: Network, event: React.MouseEvent) => {
if ("key" in event && event.key === " ") {
Expand All @@ -164,8 +139,15 @@ export const NetworkSelect = (props: NetworkSelectProps) => {
setSelection([network]);
}, [value, addSelection, setSelection]);

console.log("value", value);
useEffect(() => {
if(value.length === 0 && networks.length === 1) {
onChange([networks[0]!], false);
}
}, [networks, value]);

const open = !!anchorEl;

console.log("value", value);

return (
<>
Expand Down Expand Up @@ -207,7 +189,7 @@ export const NetworkSelect = (props: NetworkSelectProps) => {
"aria-labelledby": "basic-button",
}}
>
{multiselect && [
{networks.length > 1 && multiselect && [
<MenuItem
css={menuItemStyle}
selected={value.length === 0}
Expand All @@ -221,54 +203,31 @@ export const NetworkSelect = (props: NetworkSelectProps) => {
</MenuItem>,
<Divider key="all-divider" />
]}
{networkGroups.map((group, index) => {
const allSelected = group.networks.every(it => value.includes(it));

return [
index > 0 && <Divider key={`divider-${index}`} />,
<ListSubheader css={headerStyle} key={`subheader-${index}`}>
<div>
{group.relayChainNetwork?.displayName || "Other"}{" "}
{group.relayChainNetwork && <><br /><span style={{fontSize: 12}}>and parachains</span></>}
</div>
{multiselect && (
<Link
onClick={() => allSelected
? removeSelection(group.networks)
: addSelection(group.networks)
}
>
{allSelected ? "deselect" : "select"} all
</Link>
)}
</ListSubheader>,
group.networks.map((network) => (
<MenuItem
css={menuItemStyle}
selected={value.includes(network)}
onClick={(ev) => handleItemClick(network, ev)}
key={network.name}
>
<ListItemIcon>
<img
src={network.icon}
css={iconStyle}
/>
</ListItemIcon>
<ListItemText>{network.displayName}</ListItemText>
{multiselect && (
<Checkbox
css={checkboxStyle}
checked={value.includes(network)}
onChange={(ev, checked) => checked ? addSelection([network]) : removeSelection([network])}
onClick={(ev) => ev.stopPropagation()}
disableRipple
/>
)}
</MenuItem>
))
];
})}
{networks.map((network) => (
<MenuItem
css={menuItemStyle}
selected={value.includes(network)}
onClick={(ev) => handleItemClick(network, ev)}
key={network.name}
>
<ListItemIcon>
<img
src={network.icon}
css={iconStyle}
/>
</ListItemIcon>
<ListItemText>{network.displayName}</ListItemText>
{networks.length > 1 && multiselect && (
<Checkbox
css={checkboxStyle}
checked={value.includes(network)}
onChange={(ev, checked) => checked ? addSelection([network]) : removeSelection([network])}
onClick={(ev) => ev.stopPropagation()}
disableRipple
/>
)}
</MenuItem>
))}
</Menu>
</>
);
Expand Down
Loading

0 comments on commit 2106674

Please sign in to comment.