Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 67384eefb2 | |||
| 7d44c32419 |
@@ -15,7 +15,11 @@ pub(crate) mod routes;
|
||||
|
||||
/// Merges the routes with http information and returns it to Rocket for serving
|
||||
pub(crate) fn circulating_supply_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: routes::get_circulating_supply]
|
||||
openapi_get_routes_spec![
|
||||
settings: routes::get_full_circulating_supply,
|
||||
routes::get_total_supply,
|
||||
routes::get_circulating_supply
|
||||
]
|
||||
}
|
||||
|
||||
/// Spawn the circulating supply cache refresher.
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::circulating_supply_api::cache::CirculatingSupplyCache;
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use nym_api_requests::models::CirculatingSupplyResponse;
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
use validator_client::nyxd::Coin;
|
||||
|
||||
// TODO: this is not the best place to put it, it should be more centralised,
|
||||
// but for a quick fix, that's good enough for now...
|
||||
// (for proper solution we should be managing `NymNetworkDetails` via rocket and grabbing display exponent
|
||||
// value from the mix denom here.
|
||||
const UNYM_RATIO: f64 = 1000000.;
|
||||
|
||||
fn unym_coin_to_float_unym(coin: Coin) -> f64 {
|
||||
// our total supply can't exceed 1B so an overflow here is impossible
|
||||
// (if it happened, then we SHOULD crash)
|
||||
coin.amount as f64 / UNYM_RATIO
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply")]
|
||||
pub(crate) async fn get_circulating_supply(
|
||||
pub(crate) async fn get_full_circulating_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<CirculatingSupplyResponse>, ErrorResponse> {
|
||||
match cache.get_circulating_supply().await {
|
||||
@@ -20,3 +35,43 @@ pub(crate) async fn get_circulating_supply(
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply/total-supply-value")]
|
||||
pub(crate) async fn get_total_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<f64>, ErrorResponse> {
|
||||
let full_circulating_supply = match cache.get_circulating_supply().await {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(ErrorResponse::new(
|
||||
"unavailable",
|
||||
Status::InternalServerError,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(unym_coin_to_float_unym(
|
||||
full_circulating_supply.total_supply.into(),
|
||||
)))
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply/circulating-supply-value")]
|
||||
pub(crate) async fn get_circulating_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<f64>, ErrorResponse> {
|
||||
let full_circulating_supply = match cache.get_circulating_supply().await {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(ErrorResponse::new(
|
||||
"unavailable",
|
||||
Status::InternalServerError,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(unym_coin_to_float_unym(
|
||||
full_circulating_supply.circulating_supply.into(),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
import React from 'react';
|
||||
import { ConnectionStatusKind } from 'src/types';
|
||||
|
||||
const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => {
|
||||
if (isError && hover) {
|
||||
return '#21D072';
|
||||
}
|
||||
if (isError) {
|
||||
return '#40475C';
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 'disconnected':
|
||||
if (hover) {
|
||||
return '#FFF';
|
||||
}
|
||||
return '#BBB';
|
||||
case 'connecting':
|
||||
return '#FFF';
|
||||
case 'disconnecting':
|
||||
return '#FFF';
|
||||
default:
|
||||
// connected
|
||||
if (hover) {
|
||||
return '#E43E3E';
|
||||
}
|
||||
return '#21D072';
|
||||
}
|
||||
};
|
||||
|
||||
export const PowerButton: FCWithChildren<{
|
||||
onClick?: (status: ConnectionStatusKind) => void;
|
||||
isError?: boolean;
|
||||
disabled?: boolean;
|
||||
status: ConnectionStatusKind;
|
||||
busy?: boolean;
|
||||
}> = ({ onClick, disabled, status, isError }) => {
|
||||
const [hover, setHover] = React.useState<boolean>(false);
|
||||
|
||||
const handleClick = () => {
|
||||
if (disabled === true) {
|
||||
return;
|
||||
}
|
||||
if (onClick) {
|
||||
onClick(status);
|
||||
}
|
||||
};
|
||||
|
||||
const statusFillColor = getStatusFillColor(status, hover, Boolean(isError));
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="190"
|
||||
height="190"
|
||||
viewBox="0 0 200 200"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
onClick={handleClick}
|
||||
style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
|
||||
onMouseEnter={() => !disabled && setHover(true)}
|
||||
onMouseLeave={() => !disabled && setHover(false)}
|
||||
>
|
||||
<g transform="translate(-30, -25) ">
|
||||
<circle cx={131} cy={131} r={70} strokeWidth={2} stroke={statusFillColor} filter="url(#blur)" opacity="0.6" />
|
||||
<circle cx={131} cy={131} r={22} strokeWidth={1} stroke={statusFillColor} filter="url(#blur)" opacity="0.3" />
|
||||
<circle opacity={0.6} cx={131} cy={131} r={68.5} stroke={statusFillColor} />
|
||||
<g filter="url(#filter1_d_944_9033)">
|
||||
<circle cx={131} cy={131} r={64} fill="url(#paint1_radial_944_9033)" />
|
||||
<circle cx={131} cy={131} r={63} stroke={statusFillColor} strokeWidth={2} />
|
||||
</g>
|
||||
<g opacity={0.5} filter="url(#filter2_f_944_9033)">
|
||||
<g clipPath="url(#clip0_944_9033)">
|
||||
<path
|
||||
d="M131 113C129.9 113 129 113.9 129 115V131C129 132.1 129.9 133 131 133C132.1 133 133 132.1 133 131V115C133 113.9 132.1 113 131 113ZM141.28 118.72C140.5 119.5 140.52 120.72 141.26 121.5C143.52 123.9 144.92 127.1 145 130.64C145.18 138.3 138.84 144.9 131.18 144.98C123.36 145.1 117 138.8 117 131C117 127.32 118.42 123.98 120.74 121.48C121.48 120.7 121.48 119.48 120.72 118.72C119.92 117.92 118.62 117.94 117.86 118.76C114.96 121.84 113.14 125.94 113 130.48C112.72 140.24 120.66 148.68 130.42 148.98C140.62 149.3 149 141.12 149 130.98C149 126.24 147.16 121.96 144.16 118.76C143.4 117.94 142.08 117.92 141.28 118.72Z"
|
||||
stroke={statusFillColor}
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g clipPath="url(#clip1_944_9033)">
|
||||
<path
|
||||
d="M131 113C129.9 113 129 113.9 129 115V131C129 132.1 129.9 133 131 133C132.1 133 133 132.1 133 131V115C133 113.9 132.1 113 131 113ZM141.28 118.72C140.5 119.5 140.52 120.72 141.26 121.5C143.52 123.9 144.92 127.1 145 130.64C145.18 138.3 138.84 144.9 131.18 144.98C123.36 145.1 117 138.8 117 131C117 127.32 118.42 123.98 120.74 121.48C121.48 120.7 121.48 119.48 120.72 118.72C119.92 117.92 118.62 117.94 117.86 118.76C114.96 121.84 113.14 125.94 113 130.48C112.72 140.24 120.66 148.68 130.42 148.98C140.62 149.3 149 141.12 149 130.98C149 126.24 147.16 121.96 144.16 118.76C143.4 117.94 142.08 117.92 141.28 118.72Z"
|
||||
fill={statusFillColor}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_f_944_9033"
|
||||
x={0}
|
||||
y={0}
|
||||
width={240}
|
||||
height={240}
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity={0} result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation={40} result="effect1_foregroundBlur_944_9033" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1_d_944_9033"
|
||||
x={52}
|
||||
y={58}
|
||||
width={158}
|
||||
height={158}
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity={0} result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_944_9033" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_944_9033" result="shape" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter2_f_944_9033"
|
||||
x={97}
|
||||
y={97}
|
||||
width={68}
|
||||
height={68}
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity={0} result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation={5} result="effect1_foregroundBlur_944_9033" />
|
||||
</filter>
|
||||
<filter id="blur">
|
||||
<feGaussianBlur stdDeviation="5" />
|
||||
</filter>
|
||||
</defs>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { ConnectionStatusKind } from 'src/types';
|
||||
import './power-button.css';
|
||||
|
||||
const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => {
|
||||
if (isError && hover) {
|
||||
return '#21D072';
|
||||
}
|
||||
if (isError) {
|
||||
return '#40475C';
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 'disconnected':
|
||||
if (hover) {
|
||||
return '#FFF';
|
||||
}
|
||||
return '#BBB';
|
||||
case 'connecting':
|
||||
return '#FFF';
|
||||
case 'disconnecting':
|
||||
return '#FFF';
|
||||
default:
|
||||
// connected
|
||||
if (hover) {
|
||||
return '#E43E3E';
|
||||
}
|
||||
return '#21D072';
|
||||
}
|
||||
};
|
||||
|
||||
export const PowerButton: FCWithChildren<{
|
||||
onClick?: (status: ConnectionStatusKind) => void;
|
||||
isError?: boolean;
|
||||
disabled?: boolean;
|
||||
status: ConnectionStatusKind;
|
||||
busy?: boolean;
|
||||
}> = ({ onClick, disabled, status, isError }) => {
|
||||
const [hover, setHover] = React.useState<boolean>(false);
|
||||
|
||||
const handleClick = () => {
|
||||
if (disabled === true) {
|
||||
return;
|
||||
}
|
||||
if (onClick) {
|
||||
onClick(status);
|
||||
}
|
||||
};
|
||||
|
||||
const statusFillColor = getStatusFillColor(status, hover, Boolean(isError));
|
||||
|
||||
const getClassName = useCallback(() => {
|
||||
if (hover) {
|
||||
switch (status) {
|
||||
case 'disconnected':
|
||||
return 'expand';
|
||||
default:
|
||||
return 'contract';
|
||||
}
|
||||
}
|
||||
if (!hover) {
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
return 'expand';
|
||||
default:
|
||||
return 'contract';
|
||||
}
|
||||
}
|
||||
}, [status, hover]);
|
||||
|
||||
const buttonPulse = () => {
|
||||
if (status === 'connecting' || status === 'disconnecting') return 'pulse';
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="190"
|
||||
height="190"
|
||||
viewBox="0 0 200 200"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
onClick={handleClick}
|
||||
style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
|
||||
onMouseEnter={() => !disabled && setHover(true)}
|
||||
onMouseLeave={() => !disabled && setHover(false)}
|
||||
>
|
||||
<g transform="translate(-30, -25) ">
|
||||
<circle cx={131} cy={131} r={75} strokeWidth={4} stroke={statusFillColor} filter="url(#blur)" opacity="0.6" />
|
||||
<circle cx={131} cy={131} r={25} strokeWidth={2} stroke={statusFillColor} filter="url(#blur)" opacity="0.5" />
|
||||
<g id="Button power">
|
||||
<circle cx="131" cy="131" r="68.5" stroke={statusFillColor} stroke-width="0.5" />
|
||||
<circle id="ring-one" className={getClassName()} cx="131" cy="131" r="73" stroke={statusFillColor} />
|
||||
<circle id="ring-two" className={getClassName()} cx="131" cy="131" r="77" stroke={statusFillColor} />
|
||||
<circle id="ring-three" className={getClassName()} cx="131" cy="131" r="81" stroke={statusFillColor} />
|
||||
<circle id="ring-four" className={getClassName()} cx="131" cy="131" r="85" stroke={statusFillColor} />
|
||||
<g id="button bg">
|
||||
<circle cx="131" cy="131" r="63" stroke={statusFillColor} stroke-width="3" className={buttonPulse()} />
|
||||
</g>
|
||||
<g id="Power icon">
|
||||
<g id="Icon">
|
||||
<g id="Group 672_2">
|
||||
<g id="power_settings_new_black_24dp (1) 1_2" clip-path="url(#clip1_944_8739)">
|
||||
<path
|
||||
id="Vector_2"
|
||||
d="M131 113C129.9 113 129 113.9 129 115V131C129 132.1 129.9 133 131 133C132.1 133 133 132.1 133 131V115C133 113.9 132.1 113 131 113ZM141.28 118.72C140.5 119.5 140.52 120.72 141.26 121.5C143.52 123.9 144.92 127.1 145 130.64C145.18 138.3 138.84 144.9 131.18 144.98C123.36 145.1 117 138.8 117 131C117 127.32 118.42 123.98 120.74 121.48C121.48 120.7 121.48 119.48 120.72 118.72C119.92 117.92 118.62 117.94 117.86 118.76C114.96 121.84 113.14 125.94 113 130.48C112.72 140.24 120.66 148.68 130.42 148.98C140.62 149.3 149 141.12 149 130.98C149 126.24 147.16 121.96 144.16 118.76C143.4 117.94 142.08 117.92 141.28 118.72Z"
|
||||
fill={statusFillColor}
|
||||
className={buttonPulse()}
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="blur" width="200%" height="200%" x="-50%" y="-50%">
|
||||
<feGaussianBlur stdDeviation="12.5" />
|
||||
</filter>
|
||||
</defs>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
#ring-expand {
|
||||
animation-name: rings-expand;
|
||||
animation-duration: 0.4s;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
#ring-one.expand {
|
||||
opacity: 0.3;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
#ring-two.expand {
|
||||
opacity: 0.2;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
#ring-three.expand {
|
||||
opacity: 0.1;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
#ring-four.expand {
|
||||
opacity: 0.05;
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
#ring-one.contract {
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
#ring-two.contract {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
#ring-three.contract {
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
#ring-four.contract {
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
circle,
|
||||
path {
|
||||
transition: stroke 0.5s, fill 0.5s;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation-name: pulse;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import { IpAddressAndPort } from 'src/components/IpAddressAndPort';
|
||||
import { ServiceProvider } from 'src/types/directory';
|
||||
import { ExperimentalWarning } from 'src/components/ExperimentalWarning';
|
||||
import { ConnectionLayout } from 'src/layouts/ConnectionLayout';
|
||||
import { PowerButton } from 'src/components/PowerButton';
|
||||
import { PowerButton } from 'src/components/PowerButton/PowerButton';
|
||||
|
||||
export const Connected: FCWithChildren<{
|
||||
status: ConnectionStatusKind;
|
||||
@@ -58,7 +58,7 @@ export const Connected: FCWithChildren<{
|
||||
busy={busy}
|
||||
onClick={onConnectClick}
|
||||
isError={isError}
|
||||
disabled={status === 'connecting' || status === 'disconnecting'}
|
||||
disabled={status === 'disconnecting'}
|
||||
/>
|
||||
}
|
||||
BottomContent={
|
||||
|
||||
@@ -2,14 +2,12 @@ import React from 'react';
|
||||
import { Stack, Typography } from '@mui/material';
|
||||
import { ConnectionStatus } from 'src/components/ConnectionStatus';
|
||||
import { ConnectionTimer } from 'src/components/ConntectionTimer';
|
||||
import { useClientContext } from 'src/context/main';
|
||||
import { InfoModal } from 'src/components/InfoModal';
|
||||
import { Error } from 'src/types/error';
|
||||
import { ExperimentalWarning } from 'src/components/ExperimentalWarning';
|
||||
import { ServiceProvider, Services } from 'src/types/directory';
|
||||
import { ConnectionStatusKind } from 'src/types';
|
||||
import { ConnectionButton } from 'src/components/ConnectionButton';
|
||||
import { PowerButton } from 'src/components/PowerButton';
|
||||
import { PowerButton } from 'src/components/PowerButton/PowerButton';
|
||||
import { Box } from '@mui/system';
|
||||
import { ConnectionLayout } from 'src/layouts/ConnectionLayout';
|
||||
|
||||
@@ -22,7 +20,7 @@ export const Disconnected: FCWithChildren<{
|
||||
serviceProvider?: ServiceProvider;
|
||||
clearError: () => void;
|
||||
onConnectClick: (status: ConnectionStatusKind) => void;
|
||||
}> = ({ status, error, onConnectClick, clearError, serviceProvider }) => {
|
||||
}> = ({ status, error, onConnectClick, clearError }) => {
|
||||
return (
|
||||
<>
|
||||
{error && <InfoModal show title={error.title} description={error.message} onClose={clearError} />}
|
||||
@@ -33,7 +31,7 @@ export const Disconnected: FCWithChildren<{
|
||||
<ConnectionTimer />
|
||||
</Box>
|
||||
}
|
||||
ConnectButton={<PowerButton onClick={onConnectClick} status={status} disabled={false} />}
|
||||
ConnectButton={<PowerButton onClick={onConnectClick} status={status} disabled={status === 'connecting'} />}
|
||||
BottomContent={
|
||||
<Stack justifyContent="space-between" pt={1}>
|
||||
<Typography
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
import { PowerButton } from 'src/components/PowerButton';
|
||||
import { PowerButton } from 'src/components/PowerButton/PowerButton';
|
||||
import { ConnectionStatusKind } from 'src/types';
|
||||
|
||||
export default {
|
||||
@@ -13,7 +13,7 @@ export const Disconnected: ComponentStory<typeof PowerButton> = () => (
|
||||
);
|
||||
|
||||
export const Connecting: ComponentStory<typeof PowerButton> = () => (
|
||||
<PowerButton status={ConnectionStatusKind.connecting} />
|
||||
<PowerButton status={ConnectionStatusKind.connecting} disabled />
|
||||
);
|
||||
|
||||
export const Connected: ComponentStory<typeof PowerButton> = () => (
|
||||
@@ -21,9 +21,9 @@ export const Connected: ComponentStory<typeof PowerButton> = () => (
|
||||
);
|
||||
|
||||
export const Disconnecting: ComponentStory<typeof PowerButton> = () => (
|
||||
<PowerButton status={ConnectionStatusKind.disconnecting} />
|
||||
<PowerButton status={ConnectionStatusKind.disconnecting} disabled />
|
||||
);
|
||||
|
||||
export const Disabled: ComponentStory<typeof PowerButton> = () => (
|
||||
<PowerButton status={ConnectionStatusKind.connecting} disabled />
|
||||
<PowerButton status={ConnectionStatusKind.disconnected} disabled />
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user