Skip to content

Commit bf1b23a

Browse files
committed
Reworked aggregator add/remove to use the redux store
This heavily simplify the code, the only remaining complicted bit is the default state management
1 parent eb08003 commit bf1b23a

File tree

11 files changed

+158
-195
lines changed

11 files changed

+158
-195
lines changed

mithril-explorer/components/AggregatorSetter/AddAggregatorModal.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import React, {useState} from 'react';
22
import {Button, Form, FormGroup, Modal} from "react-bootstrap";
3+
import {useDispatch} from "react-redux";
4+
import {selectAggregator} from "../../store/settingsSlice";
35

46
export default function AddAggregatorModal(props) {
57
const [value, setValue] = useState("");
68
const [isInvalid, setIsInvalid] = useState(false);
7-
9+
const dispatch = useDispatch();
10+
811
function handleClose() {
912
props.onAskClose();
1013
setIsInvalid(false);
@@ -18,8 +21,8 @@ export default function AddAggregatorModal(props) {
1821
try {
1922
// Use the url constructor to check if the value is an url
2023
new URL(value);
21-
props.onAdd(value);
2224
handleClose();
25+
dispatch(selectAggregator(value));
2326
} catch (ex) {
2427
console.warn("invalid url", ex);
2528
setIsInvalid(true);

mithril-explorer/components/AggregatorSetter/index.js

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,26 @@
1-
import React, {useEffect, useReducer, useState} from 'react';
1+
import React, {useState} from 'react';
22
import { Button, Col, Form, InputGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
3+
import {useDispatch, useSelector} from "react-redux";
4+
import {removeCustomAggregator, selectAggregator} from "../../store/settingsSlice";
35
import AddAggregatorModal from "./AddAggregatorModal";
4-
import { initAggregatorList, aggregatorListReducer } from "./reducer";
56

67
export default function AggregatorSetter(props) {
78
const [showAddModal, toggleAddModal] = useState(false);
8-
const [state, dispatch] =
9-
useReducer(
10-
aggregatorListReducer,
11-
[props.aggregator, props.defaultAvailableAggregators],
12-
initAggregatorList);
13-
14-
useEffect(() => {
15-
const aggregator = props.aggregator;
16-
dispatch({type: 'addAggregator', aggregator: aggregator});
17-
props.onAggregatorChange(aggregator);
18-
}, [props.aggregator]);
9+
const selectedAggregator = useSelector((state) => state.settings.selectedAggregator);
10+
const availableAggregators = useSelector((state) => state.settings.availableAggregators);
11+
const canRemoveSelected = useSelector((state) => state.settings.canRemoveSelected);
12+
const dispatch = useDispatch();
1913

2014
function copySelected() {
21-
if (window.isSecureContext && props.aggregator) {
22-
navigator.clipboard.writeText(props.aggregator).then(() => { });
15+
if (window.isSecureContext && selectedAggregator) {
16+
navigator.clipboard.writeText(selectedAggregator).then(() => { });
2317
}
2418
}
2519

26-
function handleChange(aggregator) {
27-
dispatch({type: 'aggregatorSelected', aggregator: aggregator});
28-
props.onAggregatorChange(aggregator);
29-
}
30-
31-
function addAggregatorSource(aggregator) {
32-
props.onAggregatorChange(aggregator);
33-
}
34-
35-
function deleteSelectedAggregatorSource() {
36-
dispatch({type: 'deleteSelected', aggregator: props.aggregator});
37-
props.onAggregatorChange(state.availableAggregators.at(0));
38-
}
39-
4020
return (
4121
<>
4222
<AddAggregatorModal
4323
show={showAddModal}
44-
onAdd={addAggregatorSource}
4524
onAskClose={() => toggleAddModal(false)} />
4625

4726
<Form.Group as={Col} className={props.className}>
@@ -50,20 +29,20 @@ export default function AggregatorSetter(props) {
5029
<Button variant="outline-success" onClick={() => toggleAddModal(true)}>
5130
<i className="bi bi-plus-circle"></i>
5231
</Button>
53-
{state.canRemoveSelected &&
32+
{canRemoveSelected &&
5433
<>
55-
<Button variant="outline-danger" onClick={deleteSelectedAggregatorSource}>
34+
<Button variant="outline-danger" onClick={() => dispatch(removeCustomAggregator())}>
5635
<i className="bi bi-dash-circle"></i>
5736
</Button>
5837
<OverlayTrigger overlay={<Tooltip>Unofficial Aggregator</Tooltip>}>
59-
<Button variant="outline-warning" onClick={copySelected}>
38+
<Button variant="outline-warning">
6039
<i className="bi bi-exclamation-triangle"></i>
6140
</Button>
6241
</OverlayTrigger>
6342
</>
6443
}
65-
<Form.Select value={props.aggregator} onChange={e => handleChange(e.target.value)}>
66-
{state.availableAggregators.map((aggregator, index) =>
44+
<Form.Select value={selectedAggregator} onChange={(e) => dispatch(selectAggregator(e.target.value))}>
45+
{availableAggregators.map((aggregator, index) =>
6746
<option key={"agg-" + index} value={aggregator}>{aggregator}</option>
6847
)}
6948
</Form.Select>

mithril-explorer/components/AggregatorSetter/reducer.js

Lines changed: 0 additions & 80 deletions
This file was deleted.

mithril-explorer/components/CertificateModal/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
import React, { useState, useEffect } from 'react';
22
import { Badge, Button, Container, Col, ListGroup, Modal, OverlayTrigger, Row, Table, Tooltip } from "react-bootstrap";
3+
import {useSelector} from "react-redux";
34
import RawJsonButton from "../RawJsonButton";
45
import VerifiedBadge from '../VerifiedBadge';
56

67
export default function CertificateModal(props) {
78
const [certificate, setCertificate] = useState({});
9+
const aggregator = useSelector((state) => state.settings.selectedAggregator);
810

911
useEffect(() => {
1012
if (!props.hash) {
1113
return;
1214
}
1315

14-
fetch(`${props.aggregator}/certificate/${props.hash}`)
16+
fetch(`${aggregator}/certificate/${props.hash}`)
1517
.then(response => response.status === 200 ? response.json() : {})
1618
.then(data => setCertificate(data))
1719
.catch(error => {
1820
setCertificate({});
1921
console.error("Fetch certificate error:", error);
2022
});
21-
}, [props.aggregator, props.hash]);
23+
}, [aggregator, props.hash]);
2224

2325
function showPrevious() {
2426
props.onHashChange(certificate.previous_hash);
@@ -110,7 +112,7 @@ export default function CertificateModal(props) {
110112
<Button size="sm" onClick={showPrevious} className="text-break">Previous hash: {certificate.previous_hash}</Button>
111113
</>
112114
}
113-
<RawJsonButton href={`${props.aggregator}/certificate/${props.hash}`} size="sm" />
115+
<RawJsonButton href={`${aggregator}/certificate/${props.hash}`} size="sm" />
114116
</Modal.Footer>
115117
</Modal>
116118
);

mithril-explorer/components/EpochSettings/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {useSelector} from "react-redux";
55

66
export default function EpochSettings(props) {
77
const [epochSettings, setEpochSettings] = useState({});
8+
const aggregator = useSelector((state) => state.settings.selectedAggregator);
89
const autoUpdate = useSelector((state) => state.settings.autoUpdate);
910
const updateInterval = useSelector((state) => state.settings.updateInterval);
1011

@@ -14,7 +15,7 @@ export default function EpochSettings(props) {
1415
}
1516

1617
let fetchEpochSettings = () => {
17-
fetch(`${props.aggregator}/epoch-settings`)
18+
fetch(`${aggregator}/epoch-settings`)
1819
.then(response => response.status === 200 ? response.json() : {})
1920
.then(data => setEpochSettings(data))
2021
.catch(error => {
@@ -28,13 +29,13 @@ export default function EpochSettings(props) {
2829

2930
const interval = setInterval(fetchEpochSettings, updateInterval);
3031
return () => clearInterval(interval);
31-
}, [props.aggregator, updateInterval, autoUpdate]);
32+
}, [aggregator, updateInterval, autoUpdate]);
3233

3334
return (
3435
<div>
3536
<h2>
3637
Epoch Settings
37-
<RawJsonButton href={`${props.aggregator}/epoch-settings`} variant="outline-light" size="sm" />
38+
<RawJsonButton href={`${aggregator}/epoch-settings`} variant="outline-light" size="sm" />
3839
</h2>
3940

4041
<Card>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {useDispatch, useSelector} from "react-redux";
2+
import {Button, InputGroup, Form, Col} from "react-bootstrap";
3+
import {setUpdateInterval, toggleAutoUpdate} from "../../store/settingsSlice";
4+
5+
export default function IntervalSetter(props) {
6+
const autoUpdate = useSelector((state) => state.settings.autoUpdate);
7+
const updateInterval = useSelector((state) => state.settings.updateInterval);
8+
const dispatch = useDispatch();
9+
10+
return (
11+
<Form.Group as={Col} className={props.className}>
12+
<Form.Label>Update Interval:</Form.Label>
13+
<InputGroup>
14+
<Button type="button" onClick={() => dispatch(toggleAutoUpdate())} variant={autoUpdate ? "primary" : "success"}>
15+
{autoUpdate ? "Pause ⏸" : "Resume ▶"}
16+
</Button>
17+
<Form.Select value={updateInterval} onChange={(e) => dispatch(setUpdateInterval(e.target.value))}>
18+
<option value={1000}>1 seconds</option>
19+
<option value={5000}>5 seconds</option>
20+
<option value={10000}>10 seconds</option>
21+
</Form.Select>
22+
</InputGroup>
23+
</Form.Group>
24+
);
25+
}

mithril-explorer/components/PendingCertificate/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {useSelector} from "react-redux";
66

77
export default function PendingCertificate(props) {
88
const [pendingCertificate, setPendingCertificate] = useState({});
9+
const aggregator = useSelector((state) => state.settings.selectedAggregator);
910
const autoUpdate = useSelector((state) => state.settings.autoUpdate);
1011
const updateInterval = useSelector((state) => state.settings.updateInterval);
1112

@@ -15,7 +16,7 @@ export default function PendingCertificate(props) {
1516
}
1617

1718
let fetchPendingCertificate = () => {
18-
fetch(`${props.aggregator}/certificate-pending`)
19+
fetch(`${aggregator}/certificate-pending`)
1920
.then(response => response.status === 200 ? response.json() : {})
2021
.then(data => setPendingCertificate(data))
2122
.catch(error => {
@@ -29,15 +30,15 @@ export default function PendingCertificate(props) {
2930

3031
const interval = setInterval(fetchPendingCertificate, updateInterval);
3132
return () => clearInterval(interval);
32-
}, [props.aggregator, updateInterval, autoUpdate]);
33+
}, [aggregator, updateInterval, autoUpdate]);
3334

3435
return (
3536
<div className={props.className}>
3637
<h2>
3738
Pending Certificate
3839
{Object.entries(pendingCertificate).length !== 0 &&
3940
<RawJsonButton
40-
href={`${props.aggregator}/certificate-pending`}
41+
href={`${aggregator}/certificate-pending`}
4142
variant="outline-light"
4243
size="sm" />
4344
}

mithril-explorer/components/SnapshotsList/index.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function formatBytes(bytes, decimals = 2) {
2222
export default function SnapshotsList(props) {
2323
const [snapshots, setSnapshots] = useState([]);
2424
const [selectedCertificateHash, setSelectedCertificateHash] = useState(undefined);
25+
const aggregator = useSelector((state) => state.settings.selectedAggregator);
2526
const autoUpdate = useSelector((state) => state.settings.autoUpdate);
2627
const updateInterval = useSelector((state) => state.settings.updateInterval);
2728

@@ -31,7 +32,7 @@ export default function SnapshotsList(props) {
3132
}
3233

3334
let fetchSnapshots = () => {
34-
fetch(`${props.aggregator}/snapshots`)
35+
fetch(`${aggregator}/snapshots`)
3536
.then(response => response.json())
3637
.then(data => setSnapshots(data))
3738
.catch(error => {
@@ -45,7 +46,7 @@ export default function SnapshotsList(props) {
4546

4647
const interval = setInterval(fetchSnapshots, updateInterval);
4748
return () => clearInterval(interval);
48-
}, [props.aggregator, updateInterval, autoUpdate]);
49+
}, [aggregator, updateInterval, autoUpdate]);
4950

5051
function handleCertificateHashChange(hash) {
5152
setSelectedCertificateHash(hash);
@@ -58,12 +59,12 @@ export default function SnapshotsList(props) {
5859
return (
5960
<>
6061
<CertificateModal
61-
aggregator={props.aggregator}
62+
aggregator={aggregator}
6263
hash={selectedCertificateHash}
6364
onHashChange={handleCertificateHashChange} />
6465

6566
<div className={props.className}>
66-
<h2>Snapshots <RawJsonButton href={`${props.aggregator}/snapshots`} variant="outline-light" size="sm" /></h2>
67+
<h2>Snapshots <RawJsonButton href={`${aggregator}/snapshots`} variant="outline-light" size="sm" /></h2>
6768
{Object.entries(snapshots).length === 0
6869
? <p>No snapshot available</p>
6970
:
@@ -92,7 +93,7 @@ export default function SnapshotsList(props) {
9293
}
9394
<Badge bg="secondary">{snapshot.beacon.network}</Badge>
9495

95-
<RawJsonButton href={`${props.aggregator}/snapshot/${snapshot.digest}`} size="sm" className="ms-auto" />
96+
<RawJsonButton href={`${aggregator}/snapshot/${snapshot.digest}`} size="sm" className="ms-auto" />
9697
</Stack>
9798
</Card.Footer>
9899
</Card>

0 commit comments

Comments
 (0)