Files
nym/explorer/src/context/mixnode.tsx
T
Mark Sinclair e52fe65985 Network Explorer: updates to API and UI to show the active set (#1056)
* Add identicons package

* Tidy up styling and move methods into component directories with better naming

* Add mixnode status colours to theme

* Mixnode status and icon components

* Add status to mixnode types

* Add API method to get mixnode details

* Add mixnode details to state

* Add status and name+description section to mixnode detail page

* Wrap with div instead of p

* Limit width of description and link to new tab

* Limit length of link button and truncate with elipsis

* Replace `filter` with `find`

* Move mix node detail components to a location that is better named

* Refactor mixnode detail state and separate into an independent context from main state.

This prevents the mixnode detail page from showing stale data when switching between mix nodes.

* Tidy up mixnode detail page adding new state provider and a guard component to handle loading, error and not found states

* Layout changes to mixnode description header section

* Add methods to Explorer API client to get a mixnode by id, active set by status and overview summary

* Add color prop to StatsCard and make count optional

* Add optional start and end children to TableToolbar

* Tidy up naming

* Add summary overview and getting mixnodes by active set status to main state

* Add mix node status overview cards

* Add mix node status to routes

* Mixnode list has a dropdown component to select the active set status

* Clean up caching code

* Add resource to get a single mixnode by id

* Add API resources to get `active`, `inactive` and `standby` mixnodes

* Add mixnode summary to API

* Add overview summary endpoint to API

* Fix OpenAPI/swagger base url

* Make clippy happy

* Add method to get validators

* Add methods to get active and rewarded mixnodes

* Fix naming

* Move client creation to crate root

* Move cache to module

* Delete unused files

* Add validators API resource

* Add gateways API resource

* Move tasks to crate root

* Add new HTTP resources for validators and gateways to routes

* Tidy up naming and locations for mixnodes

* Add validator and gateways to state, and tidy up naming

* Add gateways and validator modules to main

* Overview shows validator and gateway summaries from state

* Bundle variable weight Open Sans fonts

* Fix up font weights and sizes

* Fix up typing

* Fix up social icons

* Fix navbar colour

* Fix paper colour in dark mode and border radius

* Fix up stats card

* Tidy up Nym icons

* Fix up overview

* Fix up spacing and padding for overview

* Add light mode shades that are darker for mixnode status values

* Review feedback
2022-01-21 11:28:59 +00:00

175 lines
5.0 KiB
TypeScript

import * as React from 'react';
import {
ApiState,
DelegationsResponse,
MixNodeDescriptionResponse,
MixNodeResponseItem,
StatsResponse,
StatusResponse,
UptimeStoryResponse,
} from 'src/typeDefs/explorer-api';
import { Api } from '../api';
import {
mixNodeResponseItemToMixnodeRowType,
MixnodeRowType,
} from '../components/MixNodes';
/**
* This context provides the state for a single mixnode by identity key.
*/
interface MixnodeState {
delegations?: ApiState<DelegationsResponse>;
description?: ApiState<MixNodeDescriptionResponse>;
mixNode?: ApiState<MixNodeResponseItem | undefined>;
mixNodeRow?: MixnodeRowType;
stats?: ApiState<StatsResponse>;
status?: ApiState<StatusResponse>;
uptimeStory?: ApiState<UptimeStoryResponse>;
}
export const MixnodeContext = React.createContext<MixnodeState>({});
export const useMixnodeContext = (): React.ContextType<typeof MixnodeContext> =>
React.useContext<MixnodeState>(MixnodeContext);
interface MixnodeContextProviderProps {
mixNodeIdentityKey: string;
}
/**
* Provides a state context for a mixnode by identity
* @param mixNodeIdentityKey The identity key of the mixnode
*/
export const MixnodeContextProvider: React.FC<MixnodeContextProviderProps> = ({
mixNodeIdentityKey,
children,
}) => {
const [mixNode, fetchMixnodeById, clearMixnodeById] = useApiState<
MixNodeResponseItem | undefined
>(mixNodeIdentityKey, Api.fetchMixnodeByID, 'Failed to fetch mixnode by id');
const [mixNodeRow, setMixnodeRow] = React.useState<
MixnodeRowType | undefined
>();
const [delegations, fetchDelegations, clearDelegations] =
useApiState<DelegationsResponse>(
mixNodeIdentityKey,
Api.fetchDelegationsById,
'Failed to fetch delegations for mixnode',
);
const [status, fetchStatus, clearStatus] = useApiState<StatusResponse>(
mixNodeIdentityKey,
Api.fetchStatusById,
'Failed to fetch mixnode status',
);
const [stats, fetchStats, clearStats] = useApiState<StatsResponse>(
mixNodeIdentityKey,
Api.fetchStatsById,
'Failed to fetch mixnode stats',
);
const [description, fetchDescription, clearDescription] =
useApiState<MixNodeDescriptionResponse>(
mixNodeIdentityKey,
Api.fetchMixnodeDescriptionById,
'Failed to fetch mixnode description',
);
const [uptimeStory, fetchUptimeHistory, clearUptimeHistory] =
useApiState<UptimeStoryResponse>(
mixNodeIdentityKey,
Api.fetchUptimeStoryById,
'Failed to fetch mixnode uptime history',
);
React.useEffect(() => {
// when the identity key changes, remove all previous data
clearMixnodeById();
clearDelegations();
clearStatus();
clearStats();
clearDescription();
clearUptimeHistory();
// fetch the mixnode, then get all the other stuff
fetchMixnodeById().then((value) => {
if (!value.data || value.error) {
setMixnodeRow(undefined);
return;
}
setMixnodeRow(mixNodeResponseItemToMixnodeRowType(value.data));
Promise.all([
fetchDelegations(),
fetchStatus(),
fetchStats(),
fetchDescription(),
fetchUptimeHistory(),
]);
});
}, [mixNodeIdentityKey]);
return (
<MixnodeContext.Provider
value={{
delegations,
mixNode,
mixNodeRow,
description,
stats,
status,
uptimeStory,
}}
>
{children}
</MixnodeContext.Provider>
);
};
/**
* Custom hook to get data from the API by passing an id to a delegate method that fetches the data asynchronously
* @param id The id to fetch
* @param fn Delegate the fetching to this method (must take `(id: string)` as a parameter)
* @param errorMessage A static error message, to use when no dynamic error message is returned
*/
function useApiState<T>(
id: string,
fn: (argId: string) => Promise<T>,
errorMessage: string,
): [ApiState<T> | undefined, () => Promise<ApiState<T>>, () => void] {
// stores the state
const [value, setValue] = React.useState<ApiState<T> | undefined>();
// clear the value
const clearValueFn = () => setValue(undefined);
// this provides a method to trigger the delegate to fetch data
const wrappedFetchFn = React.useCallback(async () => {
try {
// keep previous state and set to loading
setValue((prevState) => ({ ...prevState, isLoading: true }));
// delegate to user function to get data and set if successful
const data = await fn(id);
const newValue: ApiState<T> = {
isLoading: false,
data,
};
setValue(newValue);
return newValue;
} catch (error) {
// return the caught error or create a new error with the static error message
const newValue: ApiState<T> = {
error: error instanceof Error ? error : new Error(errorMessage),
isLoading: false,
};
setValue(newValue);
return newValue;
}
}, [setValue, fn]);
return [value || { isLoading: true }, wrappedFetchFn, clearValueFn];
}