diff --git a/package.json b/package.json index 4950cda3..05657546 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "hopr-admin", - "version": "2.1.8", + "version": "2.1.9", "private": true, "dependencies": { "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^5.0.0", - "@hoprnet/hopr-sdk": "2.1.6", + "@hoprnet/hopr-sdk": "2.1.9", "@metamask/jazzicon": "^2.0.0", "@mui/icons-material": "^5.11.16", "@mui/material": "^5.13.0", diff --git a/src/components/ConnectNode/modal.tsx b/src/components/ConnectNode/modal.tsx index 00b7e432..7849738c 100644 --- a/src/components/ConnectNode/modal.tsx +++ b/src/components/ConnectNode/modal.tsx @@ -300,6 +300,12 @@ function ConnectNodeModal(props: ConnectNodeModalProps) { apiEndpoint, }), ); + dispatch( + nodeActionsAsync.getTicketPriceThunk({ + apiToken, + apiEndpoint, + }), + ); dispatch(nodeActions.setInfo(loginInfo)); if (!apiToken || apiToken === '') { navigate(`/node/info?apiEndpoint=${formattedApiEndpoint}`); diff --git a/src/components/Modal/node/SendMessageModal.tsx b/src/components/Modal/node/SendMessageModal.tsx index cc9fc32d..a28097da 100644 --- a/src/components/Modal/node/SendMessageModal.tsx +++ b/src/components/Modal/node/SendMessageModal.tsx @@ -187,7 +187,7 @@ export const SendMessageModal = (props: SendMessageModalProps) => { }) .catch((e) => { console.log('@message err:', e); - let errMsg = `Sending message failed failed`; + let errMsg = `Sending message failed`; if (e instanceof sdkApiError && e.hoprdErrorPayload?.status) errMsg = errMsg + `.\n${e.hoprdErrorPayload.status}`; if (e instanceof sdkApiError && e.hoprdErrorPayload?.error) errMsg = errMsg + `.\n${e.hoprdErrorPayload.error}`; @@ -263,23 +263,6 @@ export const SendMessageModal = (props: SendMessageModalProps) => { return receiver; }; - const hasAlias = (peerId: string) => { - if (aliases) { - return Object.values(aliases).includes(peerId); - } - }; - - const findAlias = (peerId: string) => { - if (aliases) { - for (const alias in aliases) { - if (aliases[alias] === peerId) { - return alias; - } - } - } - return null; - }; - return ( <> { set_selectedReceiver(newValue); }} options={sendMessageAddressBook} - getOptionLabel={(peerId) => (hasAlias(peerId) ? `${findAlias(peerId)} (${peerId})` : peerId)} + getOptionLabel={(peerId) => + peerIdToAliasLink[peerId] ? `${peerIdToAliasLink[peerId]} (${peerId})` : peerId + } autoSelect renderInput={(params) => ( { const handleWithdraw = async () => { if (recipient && amount && apiEndpoint) { set_isLoading(true); + set_transactionHash(''); await dispatch( nodeActionsAsync.withdrawThunk({ amount: parseEther(amount).toString(), currency, - ethereumAddress: recipient, + address: recipient, apiEndpoint, apiToken: apiToken ? apiToken : '', + timeout: 240_000, }), ) .unwrap() @@ -216,8 +218,12 @@ const WithdrawModal = ({ initialCurrency }: WithdrawModalProps) => { value={recipient} onChange={(e) => set_recipient(e.target.value)} /> - - {isLoading && } + {transactionHash && (

Check your transaction{' '} diff --git a/src/pages/node/aliases.tsx b/src/pages/node/aliases.tsx index fd6883e9..3de1a957 100644 --- a/src/pages/node/aliases.tsx +++ b/src/pages/node/aliases.tsx @@ -161,6 +161,8 @@ function AliasesPage() { ]); }} alias={alias} + disabled={peerId === hoprAddress} + tooltip={peerId === hoprAddress ? `You can't remove this alias` : undefined} /> ), @@ -244,11 +246,13 @@ function DeleteAliasButton({ onError, onSuccess, disabled, + tooltip, }: { alias: string; onError: (e: typeof sdkApiError.prototype) => void; onSuccess: () => void; disabled?: boolean; + tooltip?: string; }) { const dispatch = useAppDispatch(); const loginData = useAppSelector((store) => store.auth.loginData); @@ -258,11 +262,15 @@ function DeleteAliasButton({ iconComponent={} aria-label="delete alias" tooltipText={ - - DELETE -
- alias -
+ tooltip ? ( + tooltip + ) : ( + + DELETE +
+ alias +
+ ) } onClick={() => { if (loginData.apiEndpoint) { diff --git a/src/pages/node/tickets.tsx b/src/pages/node/tickets.tsx index 049a88a5..2f5e1ff9 100644 --- a/src/pages/node/tickets.tsx +++ b/src/pages/node/tickets.tsx @@ -22,6 +22,7 @@ function TicketsPage() { const statisticsFetching = useAppSelector((store) => store.node.statistics.isFetching); const redeemTicketsFetching = useAppSelector((store) => store.node.redeemTickets.isFetching); const redeemTicketsErrors = useAppSelector((store) => store.node.redeemTickets.error); + const ticketPrice = useAppSelector((store) => store.node.ticketPrice.data); const loginData = useAppSelector((store) => store.auth.loginData); useEffect(() => { @@ -91,6 +92,17 @@ function TicketsPage() { style={{ marginBottom: '32px' }} > + + + + Ticket price + + + {ticketPrice ? formatEther(BigInt(ticketPrice as string)) : '-'} wxHOPR + { }; useEffect(() => { - console.log('useEffect(()', apiEndpoint, apiToken); + // console.log('useEffect(()', apiEndpoint, apiToken); if (!apiEndpoint) return; if (loginData.apiEndpoint === apiEndpoint && loginData.apiToken === apiToken) return; const formattedApiEndpoint = parseAndFormatUrl(apiEndpoint); @@ -239,7 +239,7 @@ const LayoutEnhanced = () => { dispatch(nodeActions.setApiEndpoint({ apiEndpoint: formattedApiEndpoint })); const useNode = async () => { try { - console.log('Node Admin login from router'); + // console.log('Node Admin login from router'); const loginInfo = await dispatch( authActionsAsync.loginThunk({ apiEndpoint, @@ -316,6 +316,12 @@ const LayoutEnhanced = () => { apiToken: apiToken ? apiToken : '', }), ); + dispatch( + nodeActionsAsync.getTicketPriceThunk({ + apiEndpoint, + apiToken: apiToken ? apiToken : '', + }), + ); } } catch (e) { trackGoal('ZUIBL4M8', 1); // FAILED_CONNECT_TO_NODE_BY_URL diff --git a/src/store/slices/node/actionsAsync.ts b/src/store/slices/node/actionsAsync.ts index a7bbdf69..a4571454 100644 --- a/src/store/slices/node/actionsAsync.ts +++ b/src/store/slices/node/actionsAsync.ts @@ -39,6 +39,8 @@ import { type CreateTokenResponseType, type GetPeerResponseType, type GetBalancesResponseType, + type GetTicketPricePayloadType, + type GetTicketPriceResponseType, } from '@hoprnet/hopr-sdk'; import { parseMetrics } from '../../../utils/metrics'; import { RootState } from '../..'; @@ -65,6 +67,7 @@ const { getPeers, getTicketStatistics, getToken, + getTicketPrice, fundChannel, getVersion, openChannel, @@ -890,6 +893,34 @@ const getPrometheusMetricsThunk = createAsyncThunk( + 'node/getTicketPrice', + async (payload, { rejectWithValue, dispatch }) => { + dispatch(nodeActionsFetching.setTicketPriceFetching(true)); + try { + const res = await getTicketPrice(payload); + return res; + } catch (e) { + if (e instanceof sdkApiError) { + return rejectWithValue(e); + } + return rejectWithValue({ status: JSON.stringify(e) }); + } + }, + { + condition: (_payload, { getState }) => { + const isFetching = getState().node.ticketPrice.isFetching; + if (isFetching) { + return false; + } + }, + }, +); + const isCurrentApiEndpointTheSame = createAsyncThunk( 'node/isCurrentApiEndpointTheSame', async (payload, { getState }) => { @@ -908,7 +939,7 @@ export const createAsyncReducer = (builder: ActionReducerMapBuilder { - console.log('isNodeReadyThunk', action.payload); + // console.log('isNodeReadyThunk', action.payload); if (action.payload) { state.nodeIsReady.data = action.payload; } @@ -1130,7 +1161,7 @@ export const createAsyncReducer = (builder: ActionReducerMapBuilder { if (action.meta.arg.apiEndpoint !== state.apiEndpoint) return; - console.log('getConfigurationThunk', action); + // console.log('getConfigurationThunk', action); if (action.payload) { state.configuration.data = action.payload; } @@ -1513,6 +1544,15 @@ export const createAsyncReducer = (builder: ActionReducerMapBuilder { state.redeemTickets.isFetching = false; }); + // getTicketPrice + builder.addCase(getTicketPriceThunk.fulfilled, (state, action) => { + if (action.meta.arg.apiEndpoint !== state.apiEndpoint) return; + state.ticketPrice.data = action.payload?.price || null; + state.ticketPrice.isFetching = false; + }); + builder.addCase(getTicketPriceThunk.rejected, (state) => { + state.ticketPrice.isFetching = false; + }); }; export const actionsAsync = { @@ -1543,6 +1583,7 @@ export const actionsAsync = { sendMessageThunk, pingNodeThunk, redeemTicketsThunk, + getTicketPriceThunk, createTokenThunk, deleteTokenThunk, getPrometheusMetricsThunk, diff --git a/src/store/slices/node/actionsFetching.ts b/src/store/slices/node/actionsFetching.ts index 30365351..3a4943d1 100644 --- a/src/store/slices/node/actionsFetching.ts +++ b/src/store/slices/node/actionsFetching.ts @@ -17,6 +17,7 @@ const setTicketsFetching = createAction('node/setTicketsFetching'); const setTokensFetching = createAction('node/setTokensFetching'); const setVersionFetching = createAction('node/setVersionFetching'); const setTransactionsFetching = createAction('node/setTransactionsFetching'); +const setTicketPriceFetching = createAction('node/setTicketPriceFetching'); const setRedeemTicketsFetching = createAction('node/setRedeemTicketsFetching'); export const nodeActionsFetching = { @@ -35,6 +36,7 @@ export const nodeActionsFetching = { setTokensFetching, setVersionFetching, setTransactionsFetching, + setTicketPriceFetching, setRedeemTicketsFetching, }; @@ -79,6 +81,9 @@ export const createFetchingReducer = (builder: ActionReducerMapBuilder { state.transactions.isFetching = action.payload; }), + builder.addCase(setTicketPriceFetching, (state, action) => { + state.ticketPrice.isFetching = action.payload; + }), builder.addCase(setRedeemTicketsFetching, (state, action) => { state.redeemTickets.isFetching = action.payload; }); diff --git a/src/store/slices/node/initialState.ts b/src/store/slices/node/initialState.ts index 41799766..68758213 100644 --- a/src/store/slices/node/initialState.ts +++ b/src/store/slices/node/initialState.ts @@ -202,6 +202,10 @@ type InitialState = { isFetching: boolean; error: string | undefined; }; + ticketPrice: { + data: string | null; + isFetching: boolean; + }; apiEndpoint: string | null; nodeIsReady: { data: boolean | null; @@ -340,6 +344,10 @@ export const initialState: InitialState = { isFetching: false, error: undefined, }, + ticketPrice: { + data: null, + isFetching: false, + }, links: { nodeAddressToOutgoingChannel: {}, nodeAddressToIncomingChannel: {}, diff --git a/yarn.lock b/yarn.lock index 90ecbf26..17e1e6cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -897,10 +897,10 @@ resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@hoprnet/hopr-sdk@2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@hoprnet/hopr-sdk/-/hopr-sdk-2.1.6.tgz#5672fc28db5468eb32e4e60378b4c7638cf86904" - integrity sha512-DPQN9OsXD0t3IhYTPEPg5vOV4uROtI2yhKRpUsWGofLLPtNTpYEHzqzAoJiHTESyYVB1wc+kpI8UGW4WNLyRww== +"@hoprnet/hopr-sdk@2.1.9": + version "2.1.9" + resolved "https://registry.yarnpkg.com/@hoprnet/hopr-sdk/-/hopr-sdk-2.1.9.tgz#a12ec2a89453855b301e9fb79c6541a6e4f3d8bd" + integrity sha512-m3Y7+1YnGALHATD1v5LaNGhkO3qQm2o+7NUNdy2n13qnI/10KuUQEIYUUeqLBV5+tyqG5OCA9ofk1+35uqTSnw== dependencies: debug "^4.3.4" isomorphic-ws "^5.0.0"