feat: add transfer alias page
This commit is contained in:
parent
523aac75cb
commit
67b31f03fd
10 changed files with 367 additions and 9 deletions
7
src/app/assets/svg/transfer.svg
Normal file
7
src/app/assets/svg/transfer.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.8">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.967 0.75H5.53223C2.59381 0.75 0.75 2.83122 0.75 5.77645V13.7238C0.75 16.669 2.58408 18.7502 5.53223 18.7502H13.967C16.9152 18.7502 18.7502 16.669 18.7502 13.7238V5.77645C18.7502 2.83122 16.9152 0.75 13.967 0.75Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.75008 5.77447L9.75008 13.7257" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.10335 9.43658L9.7501 5.77426L13.3969 9.43658" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
116
src/app/components/AliasTransfer/hook/useAliasTransfer.ts
Normal file
116
src/app/components/AliasTransfer/hook/useAliasTransfer.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useFeeCheck } from '../../../hooks/useFeeCheck';
|
||||
import { useInput } from '../../../hooks/useInput';
|
||||
import { fetchBackground } from '../../../utils/utils';
|
||||
import { GetAliasByAdderssParams, TransferAliasParams } from '../types';
|
||||
|
||||
const fee = 0.01;
|
||||
|
||||
export const useAliasTransfer = ({ alias }: { alias: string }) => {
|
||||
const { notEnoughFee } = useFeeCheck(fee);
|
||||
const [addressStroke, setAddressStroke] = useState<null | string>(null);
|
||||
const commentInput = useInput('', { isEmpty: false, customValidation: true });
|
||||
const addressInput = useInput('', { isEmpty: false, customError: !!addressStroke });
|
||||
const [isChecking, setIsChecking] = useState(false);
|
||||
const [isSubmitting, setSubmitting] = useState(false);
|
||||
const [isValidAddress, setValidAddress] = useState(false);
|
||||
|
||||
const [debouncedAddress, setDebouncedAddress] = useState(addressInput.value);
|
||||
const [transactionSuccess, setTransactionSuccess] = useState<null | boolean>(null);
|
||||
const [errorMsg, setErrorMsg] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
setValidAddress(false);
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setDebouncedAddress(addressInput.value);
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [addressInput.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!debouncedAddress) return;
|
||||
|
||||
const checkAddress = async () => {
|
||||
setIsChecking(true);
|
||||
|
||||
try {
|
||||
const data = await fetchBackground({
|
||||
method: 'GET_ALIAS_BY_ADDRESS',
|
||||
address: debouncedAddress,
|
||||
} as GetAliasByAdderssParams);
|
||||
|
||||
if (data.status === 'NOT_FOUND') {
|
||||
setValidAddress(true);
|
||||
setAddressStroke(null);
|
||||
} else if (data.status === 'OK') {
|
||||
setValidAddress(false);
|
||||
setAddressStroke('This wallet already has an alias');
|
||||
} else {
|
||||
setValidAddress(false);
|
||||
setAddressStroke('No wallet exists for the provided address');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
setIsChecking(false);
|
||||
}
|
||||
};
|
||||
|
||||
checkAddress();
|
||||
}, [debouncedAddress]);
|
||||
|
||||
const onTransfer = async () => {
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
const data = await fetchBackground({
|
||||
method: 'UPDATE_ALIAS',
|
||||
address: addressInput.value,
|
||||
alias,
|
||||
comment: commentInput.value,
|
||||
} as TransferAliasParams);
|
||||
|
||||
if (data?.result?.tx_id) {
|
||||
setTransactionSuccess(true);
|
||||
} else {
|
||||
setTransactionSuccess(false);
|
||||
|
||||
if (data?.error?.code === -7) {
|
||||
setErrorMsg('Not enough balance');
|
||||
} else {
|
||||
const deamonMsg = data?.error?.message;
|
||||
|
||||
setErrorMsg(deamonMsg || 'Unknown error');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const isDisabled =
|
||||
!isValidAddress ||
|
||||
addressInput.isEmpty ||
|
||||
!addressInput.inputValid ||
|
||||
isChecking ||
|
||||
isSubmitting ||
|
||||
!addressInput.value ||
|
||||
notEnoughFee ||
|
||||
Boolean(addressStroke);
|
||||
|
||||
return {
|
||||
onTransfer,
|
||||
transactionSuccess,
|
||||
errorMsg,
|
||||
isDisabled,
|
||||
addressStroke,
|
||||
commentInput,
|
||||
addressInput,
|
||||
notEnoughFee,
|
||||
fee,
|
||||
};
|
||||
};
|
||||
105
src/app/components/AliasTransfer/index.tsx
Normal file
105
src/app/components/AliasTransfer/index.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { goBack } from 'react-chrome-extension-router';
|
||||
import styles from './styles.module.scss';
|
||||
import RoutersNav from '../UI/RoutersNav/RoutersNav';
|
||||
import MyInput, { inputDataProps } from '../UI/MyInput/MyInput';
|
||||
import Button from '../UI/Button/Button';
|
||||
import InfoTooltip from '../UI/InfoTooltip';
|
||||
import { Store } from '../../store/store-reducer';
|
||||
import successImg from '../../assets/images/success-round.png';
|
||||
import errorImg from '../../assets/images/failed-round.png';
|
||||
import { useAliasTransfer } from './hook/useAliasTransfer';
|
||||
|
||||
const AliasTransfer = () => {
|
||||
const { state } = useContext(Store);
|
||||
const walletAlias = state.wallet?.alias;
|
||||
|
||||
const {
|
||||
onTransfer,
|
||||
addressStroke,
|
||||
commentInput,
|
||||
addressInput,
|
||||
fee,
|
||||
notEnoughFee,
|
||||
errorMsg,
|
||||
transactionSuccess,
|
||||
isDisabled,
|
||||
} = useAliasTransfer({
|
||||
alias: walletAlias,
|
||||
});
|
||||
|
||||
if (transactionSuccess !== null) {
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<div className={styles.main__transactionInfo}>
|
||||
<img
|
||||
className={styles.main__transactionInfo_img}
|
||||
src={transactionSuccess ? successImg : errorImg}
|
||||
alt="transaction"
|
||||
/>
|
||||
|
||||
<p className={styles.main__transactionInfo_text}>
|
||||
{transactionSuccess ? `Alias transfered!` : 'Transaction failed!'}
|
||||
</p>
|
||||
|
||||
{!transactionSuccess && (
|
||||
<p className={styles.main__transactionInfo_errorMsg}>{errorMsg}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button className={styles.main__action} onClick={goBack}>
|
||||
OK
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<RoutersNav title="Transfer Alias" />
|
||||
|
||||
<div className={styles.main__content}>
|
||||
<MyInput
|
||||
disabled
|
||||
noActiveBorder
|
||||
placeholder="Alias"
|
||||
label="Alias"
|
||||
value={`@${state.wallet.alias}`}
|
||||
/>
|
||||
|
||||
<MyInput
|
||||
stroke={addressStroke}
|
||||
placeholder="Please enter an address"
|
||||
label="Transfer to"
|
||||
inputData={addressInput as inputDataProps}
|
||||
/>
|
||||
|
||||
<MyInput
|
||||
maxLength={255}
|
||||
placeholder="Enter your comment here"
|
||||
label="Comment"
|
||||
inputData={commentInput as inputDataProps}
|
||||
noActiveBorder
|
||||
/>
|
||||
|
||||
<div className={styles.main__bottom}>
|
||||
<div className={styles.fee}>
|
||||
<h5 className={styles.fee__label}>
|
||||
Transfer fee <InfoTooltip title="Total network fee" />
|
||||
</h5>
|
||||
|
||||
<p className={`${styles.fee__value} ${notEnoughFee ? styles.error : ''}`}>
|
||||
{fee} ZANO
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button onClick={onTransfer} disabled={isDisabled}>
|
||||
Transfer alias
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default AliasTransfer;
|
||||
81
src/app/components/AliasTransfer/styles.module.scss
Normal file
81
src/app/components/AliasTransfer/styles.module.scss
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
@import 'src/app/styles/variables';
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
height: calc($appHeight - 72px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__transactionInfo {
|
||||
margin-block: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
|
||||
&_img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
&_text {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
line-height: 100%;
|
||||
color: #1f8feb;
|
||||
}
|
||||
|
||||
&_errorMsg {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
line-height: 140%;
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
&__action {
|
||||
margin-top: auto;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&__bottom {
|
||||
padding-bottom: 24px;
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.fee {
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #b6b6c4;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
|
||||
&.error {
|
||||
color: #ff6767;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/app/components/AliasTransfer/types.ts
Normal file
11
src/app/components/AliasTransfer/types.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export interface GetAliasByAdderssParams {
|
||||
method: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface TransferAliasParams {
|
||||
method: string;
|
||||
address: string;
|
||||
alias: string;
|
||||
comment?: string;
|
||||
}
|
||||
|
|
@ -125,6 +125,11 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.aliasContent {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { Dispatch, SetStateAction, useContext, useRef, useState } from 'react';
|
||||
import Decimal from 'decimal.js';
|
||||
import { styleText } from 'util';
|
||||
import copyIcon from '../../assets/svg/copy.svg';
|
||||
import dotsIcon from '../../assets/svg/dots.svg';
|
||||
import sendIcon from '../../assets/svg/send.svg';
|
||||
import editIcon from '../../assets/svg/edit.svg';
|
||||
import transferIcon from '../../assets/svg/transfer.svg';
|
||||
import settingsIcon from '../../assets/svg/settings.svg';
|
||||
import showIcon from '../../assets/svg/show.svg';
|
||||
import hideIcon from '../../assets/svg/hide.svg';
|
||||
|
|
@ -21,6 +23,7 @@ import NavLink from '../UI/NavLink/NavLink';
|
|||
import { classNames } from '../../utils/classNames';
|
||||
import { ZANO_ASSET_ID } from '../../../constants';
|
||||
import AliasManagePage from '../AliasManagePage';
|
||||
import AliasTransfer from '../AliasTransfer';
|
||||
|
||||
const Wallet = ({ setConnectOpened }: { setConnectOpened: Dispatch<SetStateAction<boolean>> }) => {
|
||||
const { state, dispatch } = useContext(Store);
|
||||
|
|
@ -111,15 +114,28 @@ const Wallet = ({ setConnectOpened }: { setConnectOpened: Dispatch<SetStateActio
|
|||
</div>
|
||||
|
||||
{state.wallet.alias && (
|
||||
<NavLink
|
||||
component={AliasManagePage}
|
||||
props={{ mode: 'edit' }}
|
||||
className="round-button"
|
||||
>
|
||||
<img width={18} height={18} src={editIcon} alt="edit icon" />
|
||||
{/* Tooltip */}
|
||||
<span>Edit alias</span>
|
||||
</NavLink>
|
||||
<div className={s.aliasWrapper__actions}>
|
||||
<NavLink
|
||||
component={AliasManagePage}
|
||||
props={{ mode: 'edit' }}
|
||||
className="round-button"
|
||||
>
|
||||
<img width={18} height={18} src={editIcon} alt="edit icon" />
|
||||
{/* Tooltip */}
|
||||
<span>Edit alias</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink component={AliasTransfer} className="round-button">
|
||||
<img
|
||||
width={18}
|
||||
height={18}
|
||||
src={transferIcon}
|
||||
alt="transfer icon"
|
||||
/>
|
||||
{/* Tooltip */}
|
||||
<span>Transfer alias</span>
|
||||
</NavLink>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={s.balanceWrapper}>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export async function fetchBackground(data: {
|
|||
success?: boolean;
|
||||
credentials?: { port: string };
|
||||
alias?: string;
|
||||
address?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
}): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
burnAsset,
|
||||
getAsset,
|
||||
updateAlias,
|
||||
getAliasByAddress,
|
||||
} from './wallet';
|
||||
import { truncateToDecimals } from '../app/utils/utils';
|
||||
|
||||
|
|
@ -278,6 +279,7 @@ const SELF_ONLY_REQUESTS = [
|
|||
'GET_TRANSFER_REQUEST',
|
||||
'REGISTER_ALIAS',
|
||||
'UPDATE_ALIAS',
|
||||
'GET_ALIAS_BY_ADDRESS',
|
||||
];
|
||||
interface Sender {
|
||||
id: string;
|
||||
|
|
@ -590,6 +592,13 @@ async function processRequest(request: RequestType, sender: Sender, sendResponse
|
|||
break;
|
||||
}
|
||||
|
||||
case 'GET_ALIAS_BY_ADDRESS': {
|
||||
getAliasByAddress(String(request.address))
|
||||
.then((res) => sendResponse(res))
|
||||
.catch(() => sendResponse({ error: 'Internal error' }));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'IONIC_SWAP_ACCEPT': {
|
||||
try {
|
||||
const swapProposalRsp = await getSwapProposalInfo(request.hex_raw_proposal);
|
||||
|
|
|
|||
|
|
@ -332,6 +332,13 @@ export const updateAlias = async ({
|
|||
return data;
|
||||
};
|
||||
|
||||
export const getAliasByAddress = async (address: string) => {
|
||||
const response = await fetchData('get_alias_by_address', address);
|
||||
const data = await response.json();
|
||||
|
||||
return data?.result;
|
||||
};
|
||||
|
||||
export const transfer = async (
|
||||
assetId = ZANO_ASSET_ID,
|
||||
destination: string | undefined,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue