Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.1.11 #642

Merged
merged 11 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 3 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public/
src/index.tsx

# Created by github pipeline
gha-creds-*.json
gha-creds-*.json

mjadach-iv marked this conversation as resolved.
Show resolved Hide resolved
src/utils/functions.ts
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ Builds the Node Admin.
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.

### `yarn build-hub`

Builds the Staking Hub.
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.

### `docker build --platform linux/amd64 -t europe-west3-docker.pkg.dev/hoprassociation/docker-images/hopr-admin .`

Builds the Node Admin docker image with the name `node-admin`.
Expand Down
39 changes: 32 additions & 7 deletions src/components/ConnectNode/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import styled from '@emotion/styled';
import { Link, useNavigate } from 'react-router-dom';

Check warning on line 3 in src/components/ConnectNode/index.tsx

View workflow job for this annotation

GitHub Actions / Build (20.x)

'Link' is defined but never used

Check warning on line 3 in src/components/ConnectNode/index.tsx

View workflow job for this annotation

GitHub Actions / Build (22.x)

'Link' is defined but never used
import { generateBase64Jazz } from '../../utils/functions';

// Components
import Modal from './modal';
Expand Down Expand Up @@ -38,8 +39,10 @@
margin-left: 8px;
width: 50px;
img {
height: 100%;
width: 100%;
height: 38px;
width: 38px;
border-radius: 50px;
background: rgb(3, 94, 91);
}
}
`;
Expand All @@ -61,6 +64,10 @@
color: #414141;
line-height: 12px;
}
.node-info-localname {
font-weight: 700;
color: #000050;
}
`;

const DropdownArrow = styled.img`
Expand Down Expand Up @@ -93,6 +100,12 @@
const openLoginModalToNode = useAppSelector((store) => store.auth.helper.openLoginModalToNode);
const peerId = useAppSelector((store) => store.node.addresses.data.hopr);
const localName = useAppSelector((store) => store.auth.loginData.localName);
const localNameToDisplay =
localName && localName.length > 17
? `${localName?.substring(0, 5)}…${localName?.substring(localName.length - 11, localName.length)}`
: localName;
const apiEndpoint = useAppSelector((store) => store.auth.loginData.apiEndpoint);
const [nodeAddressIcon, set_nodeAddressIcon] = useState<string | null>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); // State variable to hold the anchor element for the menu

const containerRef = useRef<HTMLButtonElement>(null);
Expand All @@ -111,6 +124,12 @@
};
}, []);

useEffect(() => {
if (!apiEndpoint) return;
const b64 = generateBase64Jazz(apiEndpoint);
if (b64) set_nodeAddressIcon(b64);
}, [apiEndpoint]);

useEffect(() => {
if (error) set_modalVisible(true);
}, [error]);
Expand Down Expand Up @@ -163,15 +182,21 @@
onClick={handleContainerClick}
ref={containerRef}
>
<div className="image-container">
<img src="/assets/hopr_logo.svg" />
<div
className="image-container"
id="jazz-icon-node"
>
<img src={nodeAddressIcon ?? '/assets/hopr_logo.svg'} />
</div>
{connected ? (
<>
<NodeButton>
<p className="node-info">
{peerId && `${peerId.substring(0, 6)}...${peerId.substring(peerId.length - 8, peerId.length)}`}
</p>
<span>
{localNameToDisplay && <p className="node-info node-info-localname">{localNameToDisplay}</p>}
<p className="node-info">
{peerId && `${peerId.substring(0, 6)}...${peerId.substring(peerId.length - 8, peerId.length)}`}
</p>
</span>
<div className="dropdown-icon">
<DropdownArrow src="/assets/dropdown-arrow.svg" />
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/ConnectNode/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
useEffect(() => {
// Update the Select based on loginData from the Store
if (!loginData.apiEndpoint) return;
const existingItemIndex = nodesSavedLocally.findIndex((item: any) => item.apiEndpoint === loginData.apiEndpoint);

Check warning on line 167 in src/components/ConnectNode/modal.tsx

View workflow job for this annotation

GitHub Actions / Build (20.x)

Unexpected any. Specify a different type

Check warning on line 167 in src/components/ConnectNode/modal.tsx

View workflow job for this annotation

GitHub Actions / Build (22.x)

Unexpected any. Specify a different type
if (existingItemIndex !== -1) set_nodesSavedLocallyChosenIndex(existingItemIndex.toString());
const existingItem = nodesSavedLocally[existingItemIndex] as ParsedNode;
if (existingItem && existingItem.apiToken.length > 0 && loginData.apiToken === existingItem.apiToken)
Expand Down Expand Up @@ -371,6 +371,7 @@
style={{ width: '100%' }}
removeValue={clearSingleLocal}
removeValueTooltip={'Remove node from local storage'}
showJazzIcon
/>
<Tooltip title={'Clear all node credentials from the browser local storage'}>
<span>
Expand Down
76 changes: 51 additions & 25 deletions src/future-hopr-lib-components/Select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import FormControl from '@mui/material/FormControl';
import SelectMui, { SelectProps as SelectMuiProps } from '@mui/material/Select';
import { Tooltip, IconButton } from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import { generateBase64Jazz } from '../../utils/functions';

const SFormControl = styled(FormControl)`
margin-bottom: 16px;
Expand All @@ -26,22 +27,37 @@ const SFormControl = styled(FormControl)`
display: none;
}
}

&.showJazzIcon {
.select-menu-item-text {
margin-left: 30px;
}

img.node-jazz-icon {
position: fixed;
}
mjadach-iv marked this conversation as resolved.
Show resolved Hide resolved
}
`;

interface Props extends SelectMuiProps {
removeValue?: (value: number) => void;
removeValueTooltip?: string;
showJazzIcon?: boolean;
values?: {
value: string | number;
name: string | number | null;
apiEndpoint: string | null;
disabled?: boolean;
}[];
native?: boolean;
}

const Select: React.FC<Props> = (props) => {
return (
<SFormControl style={props.style}>
<SFormControl
style={props.style}
className={`${props.showJazzIcon ? 'showJazzIcon' : ''}`}
>
<InputLabel id="select-small">{props.label}</InputLabel>
<SelectMui
labelId="select-small"
Expand All @@ -55,30 +71,40 @@ const Select: React.FC<Props> = (props) => {
MenuProps={{ disableScrollLock: true }}
>
{props.values &&
props.values.map((elem, index) => (
<MenuItem
value={elem.value}
disabled={elem.disabled}
key={`${elem.value}_${elem.name}_${index}`}
style={props.removeValue && { justifyContent: 'space-between' }}
>
{elem.name}
{props.removeValue && (
<Tooltip title={props.removeValueTooltip}>
<IconButton
aria-label="delete"
className="removeValue"
onClick={(event) => {
event.stopPropagation();
props?.removeValue?.(Number(elem.value));
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
)}
</MenuItem>
))}
props.values.map((elem, index) => {
const icon = elem.apiEndpoint && generateBase64Jazz(elem.apiEndpoint);
return (
<MenuItem
value={elem.value}
disabled={elem.disabled}
key={`${elem.value}_${elem.name}_${index}`}
id={`${elem.value}_${elem.name}_${index}`}
style={props.removeValue && { justifyContent: 'space-between' }}
>
{props.showJazzIcon && (
<img
className="node-jazz-icon"
src={icon ?? '/assets/hopr_logo.svg'}
/>
)}
<span className="select-menu-item-text">{elem.name}</span>
{props.removeValue && (
<Tooltip title={props.removeValueTooltip}>
<IconButton
aria-label="delete"
className="removeValue"
onClick={(event) => {
event.stopPropagation();
props?.removeValue?.(Number(elem.value));
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
)}
</MenuItem>
);
})}
</SelectMui>
</SFormControl>
);
Expand Down
12 changes: 12 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,15 @@ li.selected-safe {
.inline {
display: inline;
}

img.node-jazz-icon {
margin-right: 10px;
border-radius: 30px;
background: rgb(3, 94, 91);
height: 22px;
width: 22px;
}

span.select-menu-item-text {
flex-grow: 1;
}
114 changes: 111 additions & 3 deletions src/pages/node/configuration.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useState } from 'react';
import { useAppDispatch, useAppSelector } from '../../store';
import { nodeActionsAsync } from '../../store/slices/node';

// HOPR Components
import { SubpageTitle } from '../../components/SubpageTitle';
Expand All @@ -24,6 +23,8 @@ function SettingsPage() {
const dispatch = useAppDispatch();
const prevNotificationSettings = useAppSelector((store) => store.app.configuration.notifications);
const strategies = useAppSelector((store) => store.node.configuration.data?.hopr?.strategy);
const ticketPrice = useAppSelector((store) => store.node.ticketPrice.data);
const [strategiesString, set_strategiesString] = useState<string | null>(null);
const [localNotificationSettings, set_localNotificationSettings] = useState<typeof prevNotificationSettings>();

useEffect(() => {
Expand All @@ -32,6 +33,113 @@ function SettingsPage() {
}
}, [prevNotificationSettings]);

useEffect(() => {
if (strategies) {
let tmp = JSON.stringify(strategies, null, 2);

try {
if (ticketPrice) {
// min_stake_threshold
if (strategies?.strategies?.AutoFunding?.min_stake_threshold) {
const key = 'min_stake_threshold';
const min_stake_threshold = strategies.strategies.AutoFunding.min_stake_threshold.replace(' HOPR', '');
const min_stake_thresholdBigInt = BigInt(min_stake_threshold) * BigInt(1e18);
const ticketBigInt = BigInt(ticketPrice);
const ticketsBigInt = min_stake_thresholdBigInt / ticketBigInt;
const ticketsString = ticketsBigInt.toString();
const stringToReplace = `"${key}": "${strategies.strategies.AutoFunding.min_stake_threshold}"`;
if (tmp.includes(stringToReplace + ',')) {
tmp = tmp.replace(
stringToReplace + ',',
`"${key}": "${strategies.strategies.AutoFunding.min_stake_threshold}", // tickets: ${ticketsString}`,
);
} else {
tmp = tmp.replace(
stringToReplace,
`"${key}": "${strategies.strategies.AutoFunding.min_stake_threshold}" // tickets: ${ticketsString}`,
);
}
}

// funding_amount
if (strategies?.strategies?.AutoFunding?.funding_amount) {
const key = 'funding_amount';
const funding_amount = strategies.strategies.AutoFunding.funding_amount.replace(' HOPR', '');
const funding_amountBigInt = BigInt(funding_amount) * BigInt(1e18);
const ticketBigInt = BigInt(ticketPrice);
const ticketsBigInt = funding_amountBigInt / ticketBigInt;
const ticketsString = ticketsBigInt.toString();
const stringToReplace = `"${key}": "${strategies.strategies.AutoFunding.funding_amount}"`;
if (tmp.includes(stringToReplace + ',')) {
tmp = tmp.replace(
stringToReplace + ',',
`"${key}": "${strategies.strategies.AutoFunding.min_stake_threshold}", // tickets: ${ticketsString}`,
);
} else {
tmp = tmp.replace(
stringToReplace,
`"${key}": "${strategies.strategies.AutoFunding.min_stake_threshold}" // tickets: ${ticketsString}`,
mjadach-iv marked this conversation as resolved.
Show resolved Hide resolved
);
}
}

// minimum_redeem_ticket_value
if (strategies?.strategies?.AutoRedeeming?.minimum_redeem_ticket_value) {
const key = 'minimum_redeem_ticket_value';
const minimum_redeem_ticket_value = strategies.strategies.AutoRedeeming.minimum_redeem_ticket_value.replace(
' HOPR',
'',
);
const minimum_redeem_ticket_valueBigInt = BigInt(minimum_redeem_ticket_value) * BigInt(1e18);
const ticketBigInt = BigInt(ticketPrice);
const ticketsBigInt = minimum_redeem_ticket_valueBigInt / ticketBigInt;
const ticketsString = ticketsBigInt.toString();
const stringToReplace = `"${key}": "${strategies.strategies.AutoRedeeming.minimum_redeem_ticket_value}"`;
if (tmp.includes(stringToReplace + ',')) {
tmp = tmp.replace(
stringToReplace + ',',
`"${key}": "${strategies.strategies.AutoRedeeming.minimum_redeem_ticket_value}", // tickets: ${ticketsString}`,
);
} else {
tmp = tmp.replace(
stringToReplace,
`"${key}": "${strategies.strategies.AutoRedeeming.minimum_redeem_ticket_value}" // tickets: ${ticketsString}`,
);
}
}

// on_close_redeem_single_tickets_value_min
if (strategies?.strategies?.AutoRedeeming?.on_close_redeem_single_tickets_value_min) {
const key = 'on_close_redeem_single_tickets_value_min';
const on_close_redeem_single_tickets_value_min =
strategies.strategies.AutoRedeeming.on_close_redeem_single_tickets_value_min.replace(' HOPR', '');
const on_close_redeem_single_tickets_value_minBigInt =
BigInt(on_close_redeem_single_tickets_value_min) * BigInt(1e18);
const ticketBigInt = BigInt(ticketPrice);
const ticketsBigInt = on_close_redeem_single_tickets_value_minBigInt / ticketBigInt;
const ticketsString = ticketsBigInt.toString();
const stringToReplace = `"${key}": "${strategies.strategies.AutoRedeeming.on_close_redeem_single_tickets_value_min}"`;
if (tmp.includes(stringToReplace + ',')) {
tmp = tmp.replace(
stringToReplace + ',',
`"${key}": "${strategies.strategies.AutoRedeeming.on_close_redeem_single_tickets_value_min}", // tickets: ${ticketsString}`,
);
} else {
tmp = tmp.replace(
stringToReplace,
`"${key}": "${strategies.strategies.AutoRedeeming.on_close_redeem_single_tickets_value_min}" // tickets: ${ticketsString}`,
);
}
}
}
mjadach-iv marked this conversation as resolved.
Show resolved Hide resolved
} catch (e) {
console.warn('Error while counting strategies against current ticket price.', e);
}

set_strategiesString(tmp);
}
}, [strategies, ticketPrice]);

const handleSaveSettings = async () => {
if (localNotificationSettings) {
dispatch(appActions.setNotificationSettings(localNotificationSettings));
Expand Down Expand Up @@ -131,9 +239,9 @@ function SettingsPage() {
<tr>
<th>Strategies</th>
<td>
{strategies && (
{strategiesString && (
<CodeCopyBox
code={JSON.stringify(strategies, null, 2)}
code={strategiesString}
breakSpaces
/>
)}
Expand Down
Loading
Loading