Merge branch 'convert_to_ts' of https://github.com/jejolare-dev/zano-extension into convert_to_ts
This commit is contained in:
commit
389fd4e45f
61 changed files with 2827 additions and 1451 deletions
2020
package-lock.json
generated
2020
package-lock.json
generated
File diff suppressed because it is too large
Load diff
26
package.json
26
package.json
|
|
@ -51,17 +51,29 @@
|
|||
"@babel/core": "^7.21.4",
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@types/big.js": "^6.2.2",
|
||||
"@types/chrome": "^0.0.251",
|
||||
"babel-loader": "^9.1.2",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/node": "^22.10.0",
|
||||
"@types/node-forge": "^1.3.11",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@types/sha256": "^0.2.2",
|
||||
"babel-loader": "^9.2.1",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"css-loader": "^6.11.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"sass": "^1.62.0",
|
||||
"sass-loader": "^13.2.2",
|
||||
"style-loader": "^3.3.2",
|
||||
"sass-loader": "^13.3.3",
|
||||
"style-loader": "^3.3.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.7.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.79.0",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack": "^5.96.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-merge": "^5.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
/*global chrome*/
|
||||
import React from "react";
|
||||
import { useContext, useEffect, useState, useCallback } from "react";
|
||||
import { Router, goTo } from "react-chrome-extension-router";
|
||||
import AppPlug from "./components/AppPlug/AppPlug";
|
||||
|
|
@ -25,14 +26,14 @@ import {
|
|||
updateConfirmationModal,
|
||||
updateTransactionStatus,
|
||||
setConnectData,
|
||||
setWhiteList
|
||||
setWhiteList,
|
||||
} from "./store/actions";
|
||||
import { Store } from "./store/store-reducer";
|
||||
import { getZanoPrice } from "./api/coingecko";
|
||||
import "./styles/App.scss";
|
||||
import PasswordPage from "./components/PasswordPage/PasswordPage";
|
||||
import MessageSignPage from "./components/MessageSignPage/MessageSignPage";
|
||||
import AliasCreatePage from "./components/AliasCreatePage/AliasCreatePage";
|
||||
import AliasCreatePage from "./components/AliasCreatePage/AliasCreatePaget";
|
||||
import ConnectPage from "./components/ConnectPage/ConnectPage";
|
||||
import ConnectKeyUtils from "./utils/ConnectKeyUtils";
|
||||
import { defaultPort } from "./config/config";
|
||||
|
|
@ -42,10 +43,40 @@ import Big from "big.js";
|
|||
import swapModalStyles from "./styles/SwapModal.module.scss";
|
||||
import useGetAsset from "./hooks/useGetAsset";
|
||||
|
||||
// Types
|
||||
type dispatchType = () => void;
|
||||
type transferType = { transfer: { sender: string, destination: string, amount: string, asset: { ticker: string } }, id: number };
|
||||
type RequestType = { method: string; assetId: string, amount: string, destinationAddress: string, destinationChainId: string };
|
||||
type SwapRequest = {
|
||||
id: string;
|
||||
swap: {
|
||||
destinationAddress: string;
|
||||
destinationAsset: string;
|
||||
destinationAssetAmount: string;
|
||||
currentAsset: string;
|
||||
currentAssetAmount: string;
|
||||
};
|
||||
};
|
||||
type SwapProposal = {
|
||||
to_finalizer: { amount: Big }[];
|
||||
to_initiator: { amount: Big }[];
|
||||
};
|
||||
type Asset = {
|
||||
decimal_point: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
type AcceptSwapReq = {
|
||||
id: string;
|
||||
hex_raw_proposal: string;
|
||||
swapProposal: SwapProposal;
|
||||
receivingAsset: Asset;
|
||||
sendingAsset: Asset;
|
||||
};
|
||||
|
||||
function App() {
|
||||
const { state, dispatch } = useContext(Store);
|
||||
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
|
||||
const {getAssetById} = useGetAsset();
|
||||
const { getAssetById } = useGetAsset();
|
||||
|
||||
const [incorrectPassword, setIncorrectPassword] = useState(false);
|
||||
const [loggedIn, setLoggedIn] = useState(false);
|
||||
|
|
@ -61,14 +92,14 @@ function App() {
|
|||
|
||||
useEffect(() => {
|
||||
async function loadLogin() {
|
||||
const password = (await getSessionPassword());
|
||||
const password = await getSessionPassword();
|
||||
setLoggedIn(!!password);
|
||||
if (password) {
|
||||
const connectData = ConnectKeyUtils.getConnectData(password);
|
||||
|
||||
setConnectData(dispatch, {
|
||||
token: connectData.token,
|
||||
port: connectData.port,
|
||||
setConnectData(dispatch as dispatchType, {
|
||||
token: String(connectData?.token),
|
||||
port: String(connectData?.port),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +123,7 @@ function App() {
|
|||
|
||||
const closeModal = () => {
|
||||
setConfirmationModalOpen(false);
|
||||
updateConfirmationModal(dispatch, null);
|
||||
updateConfirmationModal(dispatch as dispatchType, null);
|
||||
chrome.storage?.local?.remove?.(["pendingTx"]);
|
||||
chrome.action.setBadgeText({ text: "" });
|
||||
};
|
||||
|
|
@ -105,7 +136,7 @@ function App() {
|
|||
} else {
|
||||
closeModal();
|
||||
console.log(response.status);
|
||||
updateTransactionStatus(dispatch, {
|
||||
updateTransactionStatus(dispatch as dispatchType, {
|
||||
visible: true,
|
||||
type: "error",
|
||||
code: response.status.code || 0,
|
||||
|
|
@ -128,8 +159,8 @@ function App() {
|
|||
const walletActive = await fetchBackground({
|
||||
method: "GET_WALLET_DATA",
|
||||
});
|
||||
updateWalletConnected(dispatch, !walletActive.error);
|
||||
updateLoading(dispatch, false);
|
||||
updateWalletConnected(dispatch as dispatchType, !walletActive.error);
|
||||
updateLoading(dispatch as dispatchType, false);
|
||||
};
|
||||
|
||||
const getWalletData = async () => {
|
||||
|
|
@ -137,7 +168,7 @@ function App() {
|
|||
|
||||
const walletsList = await fetchBackground({ method: "GET_WALLETS" });
|
||||
if (!walletsList.data) return;
|
||||
updateWalletsList(dispatch, walletsList.data);
|
||||
updateWalletsList(dispatch as dispatchType, walletsList.data);
|
||||
|
||||
const walletData = await fetchBackground({
|
||||
method: "GET_WALLET_DATA",
|
||||
|
|
@ -148,7 +179,7 @@ function App() {
|
|||
// console.log("WALLET DATA:");
|
||||
// console.log(walletData.data);
|
||||
|
||||
updateWalletData(dispatch, {
|
||||
updateWalletData(dispatch as dispatchType, {
|
||||
address,
|
||||
alias,
|
||||
balance,
|
||||
|
|
@ -157,7 +188,7 @@ function App() {
|
|||
});
|
||||
|
||||
console.log("wallet data updated");
|
||||
updateLoading(dispatch, false);
|
||||
updateLoading(dispatch as dispatchType, false);
|
||||
setFirstWalletLoaded(true);
|
||||
};
|
||||
|
||||
|
|
@ -176,7 +207,7 @@ function App() {
|
|||
async function updateWhiteList() {
|
||||
const whiteList = await fetchBackground({ method: "GET_WHITELIST" });
|
||||
if (whiteList.data) {
|
||||
setWhiteList(dispatch, whiteList.data);
|
||||
setWhiteList(dispatch as dispatchType, whiteList.data);
|
||||
}
|
||||
}
|
||||
updateWhiteList();
|
||||
|
|
@ -185,12 +216,12 @@ function App() {
|
|||
useEffect(() => {
|
||||
getZanoPrice().then((priceData) => {
|
||||
console.log("price data", priceData);
|
||||
updatePriceData(dispatch, priceData);
|
||||
updatePriceData(dispatch as dispatchType, priceData);
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (request, sender, sendResponse) => {
|
||||
const listener = (request: RequestType, sender: chrome.runtime.MessageSender, sendResponse: (response: { status: string }) => void) => {
|
||||
if (
|
||||
!(
|
||||
"assetId" in request &&
|
||||
|
|
@ -204,7 +235,7 @@ function App() {
|
|||
}
|
||||
|
||||
if (request.method === "BRIDGING_TRANSFER") {
|
||||
updateConfirmationModal(dispatch, {
|
||||
updateConfirmationModal(dispatch as dispatchType, {
|
||||
method: "SEND_TRANSFER",
|
||||
params: [
|
||||
request.assetId,
|
||||
|
|
@ -234,7 +265,7 @@ function App() {
|
|||
useEffect(() => {
|
||||
chrome.storage?.local?.get?.(["pendingTx"], function (result) {
|
||||
if (result.pendingTx) {
|
||||
updateConfirmationModal(dispatch, {
|
||||
updateConfirmationModal(dispatch as dispatchType, {
|
||||
method: "SEND_TRANSFER",
|
||||
params: [
|
||||
result.pendingTx.assetId,
|
||||
|
|
@ -262,22 +293,23 @@ function App() {
|
|||
method: "SET_ACTIVE_WALLET",
|
||||
id: walletId,
|
||||
});
|
||||
updateActiveWalletId(dispatch, walletId);
|
||||
updateActiveWalletId(dispatch as dispatchType, walletId);
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
|
||||
const appConnected = !!(state.connectCredentials?.token || ConnectKeyUtils.getConnectKeyEncrypted());
|
||||
const appConnected = !!(
|
||||
state.connectCredentials?.token || ConnectKeyUtils.getConnectKeyEncrypted()
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
async function modalLoad() {
|
||||
|
||||
async function getTransferRequests() {
|
||||
const transferRes = await fetchBackground({ method: "GET_TRANSFER_REQUEST" });
|
||||
const transferRes = await fetchBackground({
|
||||
method: "GET_TRANSFER_REQUEST",
|
||||
});
|
||||
const transferRequests = transferRes.data;
|
||||
const tranfserPageReqs = transferRequests.map((e) => {
|
||||
const {transfer} = e;
|
||||
const tranfserPageReqs = transferRequests.map((e: transferType) => {
|
||||
const { transfer } = e;
|
||||
return {
|
||||
id: e.id,
|
||||
method: "FINALIZE_TRANSFER_REQUEST",
|
||||
|
|
@ -285,24 +317,27 @@ function App() {
|
|||
params: [
|
||||
{
|
||||
key: "From",
|
||||
value: transfer.sender ? Formatters.walletAddress(transfer.sender) : "???"
|
||||
value: transfer.sender
|
||||
? Formatters.walletAddress(transfer.sender)
|
||||
: "???",
|
||||
},
|
||||
{
|
||||
key: "To",
|
||||
value: transfer.destination ? Formatters.walletAddress(transfer.destination) : "???"
|
||||
value: transfer.destination
|
||||
? Formatters.walletAddress(transfer.destination)
|
||||
: "???",
|
||||
},
|
||||
{
|
||||
key: "Amount",
|
||||
value: transfer.amount || "???"
|
||||
value: transfer.amount || "???",
|
||||
},
|
||||
{
|
||||
key: "Asset",
|
||||
value: transfer?.asset?.ticker || "???"
|
||||
value: transfer?.asset?.ticker || "???",
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
})
|
||||
],
|
||||
};
|
||||
});
|
||||
if (tranfserPageReqs && tranfserPageReqs.length > 0) {
|
||||
goTo(OuterConfirmation, { reqs: tranfserPageReqs });
|
||||
}
|
||||
|
|
@ -311,58 +346,64 @@ function App() {
|
|||
async function getSignRequests() {
|
||||
const response = await fetchBackground({ method: "GET_SIGN_REQUESTS" });
|
||||
const signRequests = response.data;
|
||||
|
||||
|
||||
if (signRequests && signRequests.length > 0) {
|
||||
goTo(MessageSignPage, { signRequests });
|
||||
}
|
||||
}
|
||||
|
||||
async function getAliasCreationRequests() {
|
||||
const response = await fetchBackground({ method: "GET_ALIAS_CREATE_REQUESTS" });
|
||||
console.log('alias creation requests', response);
|
||||
const response = await fetchBackground({
|
||||
method: "GET_ALIAS_CREATE_REQUESTS",
|
||||
});
|
||||
console.log("alias creation requests", response);
|
||||
const createRequests = response.data;
|
||||
|
||||
if (createRequests && createRequests.length > 0) {
|
||||
|
||||
console.log('open alias create page');
|
||||
if (createRequests && createRequests.length > 0) {
|
||||
console.log("open alias create page");
|
||||
goTo(AliasCreatePage, { createRequests });
|
||||
}
|
||||
}
|
||||
|
||||
async function getIonicSwapRequests() {
|
||||
function getSwapAmountText(amount, asset) {
|
||||
function getSwapAmountText(amount: number | Big, asset: { ticker: string }) {
|
||||
const result = (
|
||||
<>
|
||||
<span className={swapModalStyles.swapAmount}>
|
||||
{amount.toFixed()}
|
||||
</span>
|
||||
{" "}{asset.ticker}
|
||||
</span>{" "}
|
||||
{asset.ticker}
|
||||
</>
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const ionicSwapRes = await fetchBackground({ method: "GET_IONIC_SWAP_REQUESTS" });
|
||||
const ionicSwapRes = await fetchBackground({
|
||||
method: "GET_IONIC_SWAP_REQUESTS",
|
||||
});
|
||||
const swapRequests = ionicSwapRes.data;
|
||||
|
||||
const swapPageReqs = swapRequests.map(e => {
|
||||
const {swap} = e;
|
||||
const swapPageReqs = swapRequests.map((e: SwapRequest) => {
|
||||
const { swap } = e;
|
||||
|
||||
const swapParams = {};
|
||||
const swapParams: ({ address: string } | any) = {};
|
||||
|
||||
swapParams.address = swap.destinationAddress;
|
||||
|
||||
const receivingAsset = swap.destinationAsset;
|
||||
const receivingAmount = new Big(swap.destinationAssetAmount);
|
||||
|
||||
swapParams.receiving = getSwapAmountText(receivingAmount, receivingAsset);
|
||||
swapParams.receiving = getSwapAmountText(
|
||||
receivingAmount,
|
||||
receivingAsset as any
|
||||
);
|
||||
|
||||
const sendingAsset = swap.currentAsset;
|
||||
const sendingAmount = new Big(swap.currentAssetAmount);
|
||||
|
||||
swapParams.sending = getSwapAmountText(sendingAmount, sendingAsset);
|
||||
|
||||
swapParams.sending = getSwapAmountText(sendingAmount, sendingAsset as any);
|
||||
|
||||
return {
|
||||
id: e.id,
|
||||
method: "FINALIZE_IONIC_SWAP_REQUEST",
|
||||
|
|
@ -370,78 +411,95 @@ function App() {
|
|||
params: [
|
||||
{
|
||||
key: "Address",
|
||||
value: swapParams.address ? Formatters.walletAddress(swapParams.address) : "???"
|
||||
value: swapParams.address
|
||||
? Formatters.walletAddress(swapParams.address)
|
||||
: "???",
|
||||
},
|
||||
{
|
||||
key: "Sending",
|
||||
value: swapParams.sending || "???"
|
||||
value: swapParams.sending || "???",
|
||||
},
|
||||
{
|
||||
key: "Receiving",
|
||||
value: swapParams.receiving || "???"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
value: swapParams.receiving || "???",
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
const ionicSwapAcceptRes = await fetchBackground({ method: "GET_ACCEPT_IONIC_SWAP_REQUESTS" });
|
||||
const ionicSwapAcceptRes = await fetchBackground({
|
||||
method: "GET_ACCEPT_IONIC_SWAP_REQUESTS",
|
||||
});
|
||||
const acceptSwapReqs = ionicSwapAcceptRes.data;
|
||||
|
||||
console.log("ACCEPT SWAP", acceptSwapReqs);
|
||||
|
||||
const acceptPageReqs = await Promise.all(acceptSwapReqs.map(async e => {
|
||||
const hex_raw_proposal = e?.hex_raw_proposal;
|
||||
const acceptPageReqs = await Promise.all(
|
||||
acceptSwapReqs.map(async (e: AcceptSwapReq) => {
|
||||
const hex_raw_proposal = e?.hex_raw_proposal;
|
||||
|
||||
const swap = e?.swapProposal;
|
||||
const swap = e?.swapProposal;
|
||||
|
||||
const swapParams = {};
|
||||
const swapParams: ({ receiving: string } | any) = {};
|
||||
|
||||
function toBigWithDecimal(amount, decimalPoint) {
|
||||
if (amount) {
|
||||
return new Big(amount).div(new Big(10).pow(decimalPoint));
|
||||
}
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
const receivingAsset = e?.receivingAsset;
|
||||
const receivingAmount = toBigWithDecimal(swap.to_finalizer[0]?.amount, receivingAsset.decimal_point);
|
||||
|
||||
if (receivingAmount !== undefined) {
|
||||
swapParams.receiving = getSwapAmountText(receivingAmount, receivingAsset);
|
||||
function toBigWithDecimal(amount: Big, decimalPoint: number) {
|
||||
if (amount) {
|
||||
return new Big(amount).div(new Big(10).pow(decimalPoint));
|
||||
}
|
||||
}
|
||||
|
||||
const sendingAsset = e?.sendingAsset;
|
||||
const sendingAmount = toBigWithDecimal(swap.to_initiator[0]?.amount, sendingAsset.decimal_point);
|
||||
if (swap) {
|
||||
const receivingAsset = e?.receivingAsset;
|
||||
const receivingAmount = toBigWithDecimal(
|
||||
swap.to_finalizer[0]?.amount,
|
||||
receivingAsset.decimal_point
|
||||
);
|
||||
|
||||
if (sendingAmount !== undefined) {
|
||||
swapParams.sending = getSwapAmountText(sendingAmount, sendingAsset);
|
||||
if (receivingAmount !== undefined) {
|
||||
swapParams.receiving = getSwapAmountText(
|
||||
receivingAmount,
|
||||
receivingAsset as any
|
||||
);
|
||||
}
|
||||
|
||||
const sendingAsset = e?.sendingAsset;
|
||||
const sendingAmount = toBigWithDecimal(
|
||||
swap.to_initiator[0]?.amount,
|
||||
sendingAsset.decimal_point
|
||||
);
|
||||
|
||||
if (sendingAmount !== undefined) {
|
||||
swapParams.sending = getSwapAmountText(
|
||||
sendingAmount,
|
||||
sendingAsset as any
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
id: e.id,
|
||||
method: "FINALIZE_ACCEPT_IONIC_SWAP_REQUEST",
|
||||
name: "Accept Ionic Swap",
|
||||
params: [
|
||||
{
|
||||
key: "Hex Proposal",
|
||||
value: Formatters.walletAddress(hex_raw_proposal)
|
||||
},
|
||||
{
|
||||
key: "Sending",
|
||||
value: swapParams.sending || "???"
|
||||
},
|
||||
{
|
||||
key: "Receiving",
|
||||
value: swapParams.receiving || "???"
|
||||
},
|
||||
]
|
||||
}
|
||||
}));
|
||||
return {
|
||||
id: e.id,
|
||||
method: "FINALIZE_ACCEPT_IONIC_SWAP_REQUEST",
|
||||
name: "Accept Ionic Swap",
|
||||
params: [
|
||||
{
|
||||
key: "Hex Proposal",
|
||||
value: Formatters.walletAddress(hex_raw_proposal),
|
||||
},
|
||||
{
|
||||
key: "Sending",
|
||||
value: swapParams.sending || "???",
|
||||
},
|
||||
{
|
||||
key: "Receiving",
|
||||
value: swapParams.receiving || "???",
|
||||
},
|
||||
],
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const pageReqs = [...swapPageReqs, ...acceptPageReqs];
|
||||
|
||||
|
||||
if (pageReqs && pageReqs.length > 0) {
|
||||
goTo(OuterConfirmation, { reqs: pageReqs });
|
||||
}
|
||||
|
|
@ -456,7 +514,13 @@ function App() {
|
|||
if (appConnected && !connectOpened && loggedIn && state.isConnected) {
|
||||
modalLoad();
|
||||
}
|
||||
}, [appConnected, connectOpened, loggedIn, state.isConnected, state.wallet?.assets]);
|
||||
}, [
|
||||
appConnected,
|
||||
connectOpened,
|
||||
loggedIn,
|
||||
state.isConnected,
|
||||
state.wallet?.assets,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("connectCredentials", state.connectCredentials);
|
||||
|
|
@ -466,7 +530,7 @@ function App() {
|
|||
credentials: {
|
||||
token: state.connectCredentials.token,
|
||||
port: state?.connectCredentials?.port || defaultPort,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
}
|
||||
}, [state.connectCredentials]);
|
||||
|
|
@ -479,10 +543,10 @@ function App() {
|
|||
onConfirm={(password) => {
|
||||
console.log(password, comparePasswords(password));
|
||||
if (comparePasswords(password)) {
|
||||
updateLoading(dispatch, true);
|
||||
updateLoading(dispatch as dispatchType, true);
|
||||
|
||||
setTimeout(() => {
|
||||
updateLoading(dispatch, false);
|
||||
updateLoading(dispatch as dispatchType, false);
|
||||
}, 2000);
|
||||
|
||||
setLoggedIn(true);
|
||||
|
|
@ -490,7 +554,7 @@ function App() {
|
|||
const connectData = ConnectKeyUtils.getConnectData(password);
|
||||
console.log("connectData", connectData);
|
||||
if (connectData?.token) {
|
||||
setConnectData(dispatch, {
|
||||
setConnectData(dispatch as dispatchType, {
|
||||
token: connectData.token,
|
||||
port: connectData.port,
|
||||
});
|
||||
|
|
@ -544,7 +608,7 @@ function App() {
|
|||
if (connectKey)
|
||||
ConnectKeyUtils.setConnectData(
|
||||
connectKey,
|
||||
walletPort,
|
||||
String(walletPort),
|
||||
password
|
||||
);
|
||||
setLoggedIn(true);
|
||||
|
|
@ -557,4 +621,4 @@ function App() {
|
|||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
|
@ -22,7 +22,7 @@ export default function AliasCreatePage() {
|
|||
}, [createRequests]);
|
||||
|
||||
function nextRequest() {
|
||||
if (reqIndex < createRequests.length - 1) {
|
||||
if (reqIndex < createRequests.length - 1) {
|
||||
setReqIndex(reqIndex + 1);
|
||||
} else {
|
||||
goBack();
|
||||
|
|
@ -42,7 +42,7 @@ export default function AliasCreatePage() {
|
|||
setDenying(false);
|
||||
nextRequest();
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={styles.signContainer}>
|
||||
<h3 className={styles.title}>Create alias request</h3>
|
||||
|
|
@ -5,11 +5,17 @@ import questionIcon from "../../assets/svg/question.svg";
|
|||
import { Store } from "../../store/store-reducer";
|
||||
import s from "./AppPlug.module.scss";
|
||||
|
||||
const AppPlug = (props) => {
|
||||
// Define the type for props
|
||||
interface AppPlugProps {
|
||||
setConnectOpened: (isOpened: boolean) => void;
|
||||
}
|
||||
|
||||
const AppPlug: React.FC<AppPlugProps> = (props) => {
|
||||
const { state } = useContext(Store);
|
||||
|
||||
const { setConnectOpened } = props;
|
||||
|
||||
// Type of btnClasses is inferred as string
|
||||
const btnClasses = state.isLoading
|
||||
? [s.plugButton, s.hidden].join(" ")
|
||||
: s.plugButton;
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import React from "react";
|
||||
import Button from "../UI/Button/Button";
|
||||
import s from "./ConnectPage.module.scss";
|
||||
import logo from "../../assets/svg/logo.svg";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { Dispatch, SetStateAction, useContext, useEffect, useState } from "react";
|
||||
import MyInput from "../UI/MyInput/MyInput";
|
||||
import { fetchBackground, getSessionPassword } from "../../utils/utils";
|
||||
import { setConnectData } from "../../store/actions";
|
||||
|
|
@ -9,13 +10,22 @@ import { Store } from "../../store/store-reducer";
|
|||
import ConnectKeyUtils from "../../utils/ConnectKeyUtils";
|
||||
import { defaultPort } from "../../config/config";
|
||||
|
||||
interface ConnectPageProps {
|
||||
incorrectPassword: boolean;
|
||||
setIncorrectPassword: Dispatch<SetStateAction<boolean>>;
|
||||
onConfirm?: (password?: string, keyValue?: string, walletPort?: string) => void;
|
||||
passwordExists: boolean;
|
||||
setConnectOpened: Dispatch<SetStateAction<boolean>>;
|
||||
|
||||
}
|
||||
|
||||
export default function ConnectPage({
|
||||
incorrectPassword,
|
||||
setIncorrectPassword,
|
||||
onConfirm,
|
||||
passwordExists,
|
||||
setConnectOpened,
|
||||
}) {
|
||||
}: ConnectPageProps) {
|
||||
const updateSettings = !!passwordExists;
|
||||
|
||||
const { dispatch } = useContext(Store);
|
||||
|
|
@ -42,7 +52,7 @@ export default function ConnectPage({
|
|||
getExistingPort();
|
||||
}, [passwordExists]);
|
||||
|
||||
function onPasswordInput(event, repeat) {
|
||||
function onPasswordInput(event: React.ChangeEvent<HTMLInputElement>, repeat: boolean) {
|
||||
const { value } = event.currentTarget;
|
||||
setIncorrectPassword(false);
|
||||
setInvalidPassword(false);
|
||||
|
|
@ -70,7 +80,7 @@ export default function ConnectPage({
|
|||
credentials: { port: walletPort },
|
||||
});
|
||||
|
||||
setConnectData(dispatch, {
|
||||
setConnectData(dispatch as () => void, {
|
||||
token: keyValue,
|
||||
port: walletPort,
|
||||
});
|
||||
|
|
@ -89,7 +99,7 @@ export default function ConnectPage({
|
|||
}
|
||||
}
|
||||
|
||||
function onKeyInput(event) {
|
||||
function onKeyInput(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
setKeyValue(event.currentTarget.value);
|
||||
setKeyIncorrect(false);
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import arrowIcon from "../../assets/svg/arrow-shevron.svg";
|
||||
import { useCensorDigits } from "../../hooks/useCensorDigits";
|
||||
|
|
@ -22,11 +23,11 @@ const Header = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const switchWallet = (id) => {
|
||||
const switchWallet = (id: number | undefined) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.storage.local.set({ key: id }, function () {
|
||||
updateLoading(dispatch, true);
|
||||
updateActiveWalletId(dispatch, id);
|
||||
updateLoading(dispatch as () => void, true);
|
||||
updateActiveWalletId(dispatch as () => void, String(id));
|
||||
|
||||
fetchBackground({
|
||||
method: "SET_ACTIVE_WALLET",
|
||||
|
|
@ -34,7 +35,7 @@ const Header = () => {
|
|||
});
|
||||
|
||||
console.log("Active wallet set to", id);
|
||||
setTimeout(() => updateLoading(dispatch, false), 1000);
|
||||
setTimeout(() => updateLoading(dispatch as () => void, false), 1000);
|
||||
});
|
||||
|
||||
toggleDropdown();
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React from "react";
|
||||
import { getCurrent, goBack } from "react-chrome-extension-router";
|
||||
import Button, { ButtonThemes } from "../UI/Button/Button";
|
||||
import styles from "./MessageSignPage.module.scss";
|
||||
|
|
@ -21,7 +22,7 @@ export default function MessageSignPage() {
|
|||
}, [signRequests]);
|
||||
|
||||
function nextRequest() {
|
||||
if (reqIndex < signRequests.length - 1) {
|
||||
if (reqIndex < signRequests.length - 1) {
|
||||
setReqIndex(reqIndex + 1);
|
||||
} else {
|
||||
goBack();
|
||||
|
|
@ -41,7 +42,7 @@ export default function MessageSignPage() {
|
|||
setDenying(false);
|
||||
nextRequest();
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={styles.signContainer}>
|
||||
<h3 className={styles.title}>Sign Request</h3>
|
||||
|
|
@ -1,10 +1,16 @@
|
|||
import React, { useCallback, useContext } from "react";
|
||||
import cls from "../../components/ModalConfirmation/ModalConfirmation.module.scss";
|
||||
import Modal from "../../components/UI/Modal/Modal";
|
||||
import Modal from "../UI/Modal/Modal";
|
||||
import Button, { ButtonThemes } from "../UI/Button/Button";
|
||||
import { Store } from "../../store/store-reducer";
|
||||
|
||||
const ModalConfirmation = ({ isOpen, onClose, onConfirm }) => {
|
||||
interface ModalConfirmationProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const ModalConfirmation = ({ isOpen, onClose, onConfirm }: ModalConfirmationProps) => {
|
||||
const { state } = useContext(Store);
|
||||
const { method, params } = state.confirmationModal || {};
|
||||
|
||||
|
|
@ -30,7 +36,7 @@ const ModalConfirmation = ({ isOpen, onClose, onConfirm }) => {
|
|||
<div className={cls.label}>params:</div>
|
||||
<div className={cls.value}>
|
||||
{params &&
|
||||
params.map((param) => <span key={param}>{param}</span>)}
|
||||
params.map((param: string) => <span key={param}>{param}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useCallback, useContext } from "react";
|
||||
import cls from "./ModalTransactionStatus.module.scss";
|
||||
import Modal from "../../components/UI/Modal/Modal";
|
||||
import Modal from "../UI/Modal/Modal";
|
||||
import { Store } from "../../store/store-reducer";
|
||||
import errorImage from "../../assets/svg/plus.svg";
|
||||
import { updateTransactionStatus } from "../../store/actions";
|
||||
|
|
@ -13,7 +13,7 @@ const ModalTransactionStatus = () => {
|
|||
const { visible, type, code, message } = state?.transactionStatus;
|
||||
|
||||
const closeHandler = useCallback(() => {
|
||||
updateTransactionStatus(dispatch, (prevState) => ({
|
||||
updateTransactionStatus(dispatch as () => void, (prevState: object) => ({
|
||||
...prevState,
|
||||
isVisible: false,
|
||||
}));
|
||||
|
|
@ -34,7 +34,7 @@ const ModalTransactionStatus = () => {
|
|||
if (type === "error") {
|
||||
return (
|
||||
<div className={cls.error}>
|
||||
<div className={classNames(cls.icon, {}, [[cls.redColor]])}>
|
||||
<div className={classNames(cls.icon, {}, [cls.redColor])}>
|
||||
<img src={errorImage} alt="error" />
|
||||
</div>
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ const ModalTransactionStatus = () => {
|
|||
<Modal
|
||||
width={296}
|
||||
isOpen={visible}
|
||||
onClose={type !== "loading" && closeHandler}
|
||||
onClose={type !== "loading" ? closeHandler : () => { }}
|
||||
>
|
||||
<div className={cls.ModalTransactionStatus}>
|
||||
<Loading />
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
import React from "react";
|
||||
import { getCurrent, goBack } from "react-chrome-extension-router";
|
||||
import Button, { ButtonThemes } from "../UI/Button/Button";
|
||||
import styles from "./OuterConfirmation.module.scss";
|
||||
import { useState, useEffect } from "react";
|
||||
import { fetchBackground } from "../../utils/utils";
|
||||
|
||||
interface ParamsType {
|
||||
key: number;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const OuterConfirmation = () => {
|
||||
const { props } = getCurrent();
|
||||
const { reqs } = props;
|
||||
|
|
@ -27,7 +33,7 @@ const OuterConfirmation = () => {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function acceptClick() {
|
||||
setAccepting(true);
|
||||
await fetchBackground({ method, id, success: true });
|
||||
|
|
@ -50,7 +56,7 @@ const OuterConfirmation = () => {
|
|||
<h5>{name}</h5>
|
||||
<div className={styles.table}>
|
||||
{
|
||||
params.map((param) => {
|
||||
params.map((param: ParamsType) => {
|
||||
|
||||
return (
|
||||
<div className={styles.row} key={param.key}>
|
||||
|
|
@ -1,10 +1,17 @@
|
|||
import React, { ChangeEvent } from "react";
|
||||
import s from "./PasswordPage.module.scss";
|
||||
import logo from "../../assets/svg/logo.svg";
|
||||
import MyInput from "../UI/MyInput/MyInput";
|
||||
import Button from "../UI/Button/Button";
|
||||
import { useState } from "react";
|
||||
|
||||
function PasswordPage(props) {
|
||||
interface PasswordPageProps {
|
||||
onConfirm: (password: string) => void;
|
||||
incorrectPassword: boolean;
|
||||
setIncorrectPassword: (value: boolean) => void;
|
||||
}
|
||||
|
||||
function PasswordPage(props: PasswordPageProps) {
|
||||
const {
|
||||
onConfirm,
|
||||
incorrectPassword,
|
||||
|
|
@ -13,7 +20,7 @@ function PasswordPage(props) {
|
|||
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
function onInputChange(event) {
|
||||
function onInputChange(event: ChangeEvent<HTMLInputElement>) {
|
||||
setIncorrectPassword(false);
|
||||
setPassword(event.currentTarget.value);
|
||||
}
|
||||
|
|
@ -24,11 +31,11 @@ function PasswordPage(props) {
|
|||
|
||||
return (
|
||||
<div className={s.passwordPage}>
|
||||
|
||||
<img
|
||||
|
||||
<img
|
||||
className={s.logoImage}
|
||||
src={logo}
|
||||
alt="Zano"
|
||||
src={logo}
|
||||
alt="Zano"
|
||||
/>
|
||||
<p>Enter your password</p>
|
||||
<div className={s.inputPanel}>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React from "react";
|
||||
import { useContext } from "react";
|
||||
import crossIcon from "../../../assets/svg/cross.svg";
|
||||
import bitcoinIcon from "../../../assets/tokens-svg/bitcoin.svg";
|
||||
|
|
@ -9,7 +10,15 @@ import { Store } from "../../../store/store-reducer";
|
|||
import s from "./Assets.module.scss";
|
||||
import Decimal from "decimal.js";
|
||||
|
||||
const getIconImage = (asset) => {
|
||||
interface Asset {
|
||||
name: string;
|
||||
ticker: string;
|
||||
balance: number;
|
||||
lockedBalance?: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
const getIconImage = (asset: Asset) => {
|
||||
switch (asset.name) {
|
||||
case "Zano":
|
||||
return <img src={zanoIcon} alt="ZanoIcon" />;
|
||||
|
|
@ -30,7 +39,7 @@ const Assets = () => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
{state.wallet.assets.map((asset) => {
|
||||
{(state.wallet.assets).map((asset) => {
|
||||
const fiatBalance = (
|
||||
Number(asset.balance) * state.priceData.price
|
||||
).toFixed(2);
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React from "react";
|
||||
import { useContext } from "react";
|
||||
import Big from "big.js";
|
||||
import LoadingIcon from "../../../assets/svg/loading.svg";
|
||||
|
|
@ -9,9 +10,18 @@ import s from "./History.module.scss";
|
|||
import NavLink from '../../UI/NavLink/NavLink';
|
||||
import useGetAsset from "../../../hooks/useGetAsset";
|
||||
|
||||
interface HistoryItemProps {
|
||||
transfer: {
|
||||
assetId: string;
|
||||
amount: string;
|
||||
incoming: boolean;
|
||||
};
|
||||
fee: string;
|
||||
isInitiator: boolean;
|
||||
}
|
||||
|
||||
const HistoryItem = ({ transfer, fee, isInitiator }) => {
|
||||
const {getAssetById} = useGetAsset();
|
||||
const HistoryItem = ({ transfer, fee, isInitiator }: HistoryItemProps) => {
|
||||
const { getAssetById } = useGetAsset();
|
||||
|
||||
if (transfer.amount === fee) return null;
|
||||
const amount = new Big(transfer.amount);
|
||||
|
|
@ -25,11 +35,11 @@ const HistoryItem = ({ transfer, fee, isInitiator }) => {
|
|||
<p>
|
||||
<span>
|
||||
{transfer.assetId ===
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
? !isInitiator
|
||||
? amount.toFixed()
|
||||
: amount.minus(fixedFee).toFixed()
|
||||
: amount.toFixed()
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
? !isInitiator
|
||||
? amount.toFixed()
|
||||
: amount.minus(fixedFee).toFixed()
|
||||
: amount.toFixed()
|
||||
}
|
||||
</span>
|
||||
{" "}
|
||||
|
|
@ -61,8 +71,8 @@ const History = () => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{tx.transfers.map((transfer) => (
|
||||
<HistoryItem transfer={transfer} fee={tx.fee} isInitiator={tx.isInitiator} />
|
||||
{tx.transfers?.map((transfer) => (
|
||||
<HistoryItem transfer={transfer as any} fee={String(tx.fee)} isInitiator={Boolean(tx.isInitiator)} />
|
||||
))}
|
||||
<span className={s.historyAddress}>{tx.txHash}</span>
|
||||
</NavLink>
|
||||
|
|
@ -72,4 +82,4 @@ const History = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default History;
|
||||
export default History;
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React, { MouseEvent } from "react";
|
||||
import { useState } from "react";
|
||||
import Assets from "./Assets/Assets";
|
||||
import History from "./History/History";
|
||||
|
|
@ -11,9 +12,9 @@ const TokensTabs = () => {
|
|||
{ label: "history", content: <History /> },
|
||||
];
|
||||
|
||||
const toggleTabs = (e) => {
|
||||
if (activeTab !== e.target.value) {
|
||||
setActiveTab(Number(e.target.value));
|
||||
const toggleTabs = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (activeTab !== Number(e.currentTarget.value)) {
|
||||
setActiveTab(Number(e.currentTarget.value));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -8,23 +8,54 @@ import RoutersNav from "../UI/RoutersNav/RoutersNav";
|
|||
import { Store } from "../../store/store-reducer";
|
||||
import styles from "./TransactionDetails.module.scss";
|
||||
|
||||
const TransactionDetails = (props) => {
|
||||
type Transfer = {
|
||||
amount: string;
|
||||
assetId: string;
|
||||
incoming: boolean;
|
||||
};
|
||||
|
||||
type TransactionDetailsProps = {
|
||||
transfers?: Transfer[];
|
||||
fee: string;
|
||||
addresses?: string[];
|
||||
txHash: string;
|
||||
blobSize: number;
|
||||
timestamp: string;
|
||||
height: number;
|
||||
paymentId?: string | null;
|
||||
comment: string;
|
||||
isInitiator?: boolean;
|
||||
};
|
||||
|
||||
type TableRowProps = {
|
||||
label: string;
|
||||
value?: string | number;
|
||||
copyButton?: boolean;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type WhitelistedAssetType = {
|
||||
asset_id: string;
|
||||
ticker: string;
|
||||
};
|
||||
|
||||
const TransactionDetails: React.FC<TransactionDetailsProps> = (props) => {
|
||||
const { state } = useContext(Store);
|
||||
const { copyToClipboard, SuccessCopyModal } = useCopy();
|
||||
const { copyToClipboard } = useCopy(); // removed: SuccessCopyModal
|
||||
|
||||
useEffect(() => {
|
||||
document.body.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const TableRow = ({ label, value, copyButton, children }) => {
|
||||
const TableRow: React.FC<TableRowProps> = ({ label, value, copyButton, children }) => {
|
||||
return (
|
||||
<div className={`${copyButton && "table__row_button"} table__row`}>
|
||||
<div className="table__label">
|
||||
{label}:
|
||||
{copyButton && (
|
||||
{copyButton && value && (
|
||||
<button
|
||||
className="round-button"
|
||||
onClick={() => copyToClipboard(value)}
|
||||
onClick={() => copyToClipboard(value.toString())}
|
||||
>
|
||||
<img src={copyIcon} alt="copy icon" />
|
||||
</button>
|
||||
|
|
@ -38,30 +69,29 @@ const TransactionDetails = (props) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
{SuccessCopyModal}
|
||||
{/* {SuccessCopyModal} */}
|
||||
|
||||
<RoutersNav title="Transaction details" />
|
||||
|
||||
<div className="table">
|
||||
<TableRow label="Transfers">
|
||||
<div className={styles.transaction__transfers}>
|
||||
{props.transfers.map((transfer) => {
|
||||
{props?.transfers?.map((transfer, index) => {
|
||||
if (transfer.amount === props.fee) return null;
|
||||
const amount = new Big(transfer.amount);
|
||||
const fixedFee = new Big(props.fee);
|
||||
return (
|
||||
<div className={styles.transaction__transfer}>
|
||||
<div key={index} className={styles.transaction__transfer}>
|
||||
<p className="table__value">
|
||||
{transfer.assetId ===
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
? !props.isInitiator
|
||||
? amount.toFixed()
|
||||
: amount.minus(fixedFee).toFixed()
|
||||
: amount.toFixed()
|
||||
}{" "}
|
||||
: amount.toFixed()}{" "}
|
||||
{
|
||||
state.whitelistedAssets.find(
|
||||
(asset) => asset.asset_id === transfer.assetId
|
||||
(state.whitelistedAssets as any).find(
|
||||
(asset: WhitelistedAssetType) => asset.asset_id === transfer.assetId
|
||||
)?.ticker ?? "***"
|
||||
}
|
||||
</p>
|
||||
|
|
@ -75,7 +105,6 @@ const TransactionDetails = (props) => {
|
|||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
</TableRow>
|
||||
<TableRow label="Fee" value={props.fee + " ZANO"} />
|
||||
{props.addresses && (
|
||||
|
|
@ -92,7 +121,7 @@ const TransactionDetails = (props) => {
|
|||
{props.paymentId ? (
|
||||
<TableRow label="Payment Id" value={props.paymentId} copyButton />
|
||||
) : (
|
||||
<TableRow label="Payment Id" value={props.paymentId} />
|
||||
<TableRow label="Payment Id" value={props.paymentId ?? "N/A"} />
|
||||
)}
|
||||
<TableRow label="Comment" value={props.comment} />
|
||||
</div>
|
||||
|
|
@ -4,8 +4,15 @@ import logo from "../../../assets/svg/logo.svg";
|
|||
import { Store } from "../../../store/store-reducer";
|
||||
import s from "./AppLoader.module.scss";
|
||||
|
||||
const AppLoader = ({ isSmall, firstWalletLoaded, loggedIn }) => {
|
||||
interface AppLoaderProps {
|
||||
isSmall?: boolean;
|
||||
firstWalletLoaded: boolean;
|
||||
loggedIn: boolean;
|
||||
}
|
||||
|
||||
const AppLoader: React.FC<AppLoaderProps> = ({ isSmall, firstWalletLoaded, loggedIn }) => {
|
||||
const { state } = useContext(Store);
|
||||
|
||||
const loaderClasses = isSmall ? [s.loader, s.small].join(" ") : s.loader;
|
||||
|
||||
return (
|
||||
|
|
@ -26,4 +33,4 @@ const AppLoader = ({ isSmall, firstWalletLoaded, loggedIn }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default AppLoader;
|
||||
export default AppLoader;
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import React, { memo } from "react";
|
||||
import cls from "./Button.module.scss";
|
||||
import { classNames } from "../../../utils/classNames";
|
||||
|
||||
export const ButtonThemes = {
|
||||
Primary: "primary",
|
||||
Outline: "outline",
|
||||
Clear: "clear",
|
||||
};
|
||||
|
||||
export const Button = memo((props) => {
|
||||
const {
|
||||
className,
|
||||
children,
|
||||
theme = ButtonThemes.Primary,
|
||||
href,
|
||||
fullWidth,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
className={classNames(cls.Button, {}, [className, cls[theme]])}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames(cls.Button, {}, [className, cls[theme]])}
|
||||
type="button"
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
export default Button;
|
||||
63
src/app/components/UI/Button/Button.tsx
Normal file
63
src/app/components/UI/Button/Button.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import React, { memo, ButtonHTMLAttributes, AnchorHTMLAttributes } from "react";
|
||||
import cls from "./Button.module.scss";
|
||||
import { classNames } from "../../../utils/classNames";
|
||||
|
||||
interface ButtonBaseProps {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
theme?: keyof ThemeProps | string;
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
interface ThemeProps {
|
||||
Primary: "primary";
|
||||
Outline: "outline";
|
||||
Clear: "clear";
|
||||
}
|
||||
|
||||
export const ButtonThemes: ThemeProps = {
|
||||
Primary: "primary",
|
||||
Outline: "outline",
|
||||
Clear: "clear",
|
||||
};
|
||||
|
||||
type ButtonProps = ButtonBaseProps &
|
||||
(
|
||||
| (ButtonHTMLAttributes<HTMLButtonElement> & { href?: never })
|
||||
| (AnchorHTMLAttributes<HTMLAnchorElement> & { href: string })
|
||||
);
|
||||
|
||||
export const Button = memo((props: ButtonProps) => {
|
||||
const {
|
||||
className,
|
||||
children,
|
||||
theme = ButtonThemes.Primary,
|
||||
href,
|
||||
fullWidth,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
className={classNames(cls.Button, {}, [className, cls[theme]])}
|
||||
{...(otherProps as AnchorHTMLAttributes<HTMLAnchorElement>)}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames(cls.Button, {}, [className, cls[theme]])}
|
||||
type="button"
|
||||
{...(otherProps as ButtonHTMLAttributes<HTMLButtonElement>)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
export default Button;
|
||||
|
|
@ -1,9 +1,17 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React, { MouseEvent, ReactNode, useCallback, useEffect, useState } from "react";
|
||||
import cls from "./Modal.module.scss";
|
||||
import { createPortal } from "react-dom";
|
||||
import { classNames } from '../../../utils/classNames';
|
||||
|
||||
const Modal = (props) => {
|
||||
interface ModalProps {
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
width?: string | number;
|
||||
}
|
||||
|
||||
const Modal = (props: ModalProps) => {
|
||||
const { className, children, isOpen, onClose, width } = props;
|
||||
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
|
@ -21,7 +29,7 @@ const Modal = (props) => {
|
|||
}, [onClose]);
|
||||
|
||||
const onKeyDown = useCallback(
|
||||
(e) => {
|
||||
(e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") {
|
||||
closeHandler();
|
||||
}
|
||||
|
|
@ -39,7 +47,7 @@ const Modal = (props) => {
|
|||
};
|
||||
}, [isOpen, onKeyDown]);
|
||||
|
||||
const onContentClick = (e) => {
|
||||
const onContentClick = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
|
@ -54,7 +62,7 @@ const Modal = (props) => {
|
|||
return createPortal(
|
||||
<div className={classNames(cls.Modal, mods, [className])}>
|
||||
<div onClick={closeHandler} className={cls.wrapper}>
|
||||
<div onClick={onContentClick} style={{width}} className={cls.content}>
|
||||
<div onClick={onContentClick} style={{ width }} className={cls.content}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
import React from "react";
|
||||
import React, { InputHTMLAttributes } from "react";
|
||||
import nextId from "react-id-generator";
|
||||
import s from "./MyCheckbox.module.scss";
|
||||
|
||||
const MyCheckbox = ({ label, ...props }) => {
|
||||
interface MyCheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label: string;
|
||||
}
|
||||
|
||||
const MyCheckbox: React.FC<MyCheckboxProps> = ({ label, ...props }) => {
|
||||
const id = nextId();
|
||||
|
||||
return (
|
||||
|
|
@ -1,9 +1,29 @@
|
|||
import React, { memo } from "react";
|
||||
import React, { memo, ChangeEvent, InputHTMLAttributes } from "react";
|
||||
import nextId from "react-id-generator";
|
||||
import cls from "./MyInput.module.scss";
|
||||
import { classNames } from "../../../utils/classNames";
|
||||
|
||||
const MyInput = memo((props) => {
|
||||
interface inputDataProps {
|
||||
value?: string;
|
||||
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
onInput?: (value: string) => void;
|
||||
inputValid?: boolean;
|
||||
onBlur?: () => void;
|
||||
isDirty?: boolean;
|
||||
isFilled?: boolean;
|
||||
}
|
||||
|
||||
interface MyInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string;
|
||||
inputData?: inputDataProps;
|
||||
isValid?: boolean;
|
||||
noActiveBorder?: boolean;
|
||||
isError?: boolean;
|
||||
noValidation?: boolean;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const MyInput: React.FC<MyInputProps> = memo((props) => {
|
||||
const id = nextId();
|
||||
const {
|
||||
label,
|
||||
|
|
@ -15,18 +35,18 @@ const MyInput = memo((props) => {
|
|||
noValidation,
|
||||
...otherProps
|
||||
} = props;
|
||||
const { value, onChange, onInput, inputValid, onBlur, isDirty, isFilled } =
|
||||
inputData;
|
||||
|
||||
const onInputHandler = (e) => {
|
||||
const { value, onChange, onInput, inputValid, onBlur, isDirty, isFilled } = inputData || {};
|
||||
|
||||
const onInputHandler = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (type === "number" && !noValidation) {
|
||||
const newValue = e.target.value
|
||||
.replace(/[^0-9.]/g, "")
|
||||
.replace(/(\..*?)\..*/g, "$1")
|
||||
.replace(/^0[^.]/, "0");
|
||||
onInput(newValue);
|
||||
if (onInput) onInput(newValue);
|
||||
} else {
|
||||
onChange(e);
|
||||
if (onChange) onChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -40,7 +60,7 @@ const MyInput = memo((props) => {
|
|||
<div className={cls.myInput}>
|
||||
<input
|
||||
onBlur={onBlur}
|
||||
onChange={(e) => onInputHandler(e)}
|
||||
onChange={onInputHandler}
|
||||
type={type}
|
||||
id={id}
|
||||
value={value}
|
||||
|
|
@ -2,7 +2,14 @@ import React from "react";
|
|||
import { Link } from "react-chrome-extension-router";
|
||||
import { classNames } from "../../../utils/classNames";
|
||||
|
||||
const NavLink = ({ component, children, className, ...props }) => {
|
||||
interface NavLinkProps {
|
||||
component: React.ComponentType<any>;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
props?: any;
|
||||
}
|
||||
|
||||
const NavLink = ({ component, children, className, ...props }: NavLinkProps) => {
|
||||
const scrollHandler = () => {
|
||||
document.body.scrollTop = 0;
|
||||
};
|
||||
|
|
@ -3,9 +3,14 @@ import { goBack } from "react-chrome-extension-router";
|
|||
import backIcon from "../../../assets/svg/arrow-back.svg";
|
||||
import s from "./RoutersNav.module.scss";
|
||||
|
||||
const RoutersNav = ({ title, onClick }) => {
|
||||
interface RoutersNavProps {
|
||||
title: string;
|
||||
onClick?: "none" | (() => void | undefined);
|
||||
}
|
||||
|
||||
const RoutersNav = ({ title, onClick }: RoutersNavProps) => {
|
||||
const clickHandler = () => {
|
||||
if (onClick) {
|
||||
if (typeof onClick === "function") {
|
||||
onClick();
|
||||
} else {
|
||||
goBack();
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React, { Dispatch, SetStateAction } from "react";
|
||||
import { useContext, useRef, useState } from "react";
|
||||
import copyIcon from "../../assets/svg/copy.svg";
|
||||
import dotsIcon from "../../assets/svg/dots.svg";
|
||||
|
|
@ -12,14 +13,14 @@ import { useCensorDigits } from "../../hooks/useCensorDigits";
|
|||
import { useCopy } from "../../hooks/useCopy";
|
||||
import { Store } from "../../store/store-reducer";
|
||||
import { updateBalancesHidden, updateDisplay } from "../../store/actions";
|
||||
import ModalTransactionStatus from "../../components/ModalTransactionStatus/ModalTransactionStatus";
|
||||
import ModalTransactionStatus from "../ModalTransactionStatus/ModalTransactionStatus";
|
||||
import WalletSend from "../WalletSend/WalletSend";
|
||||
import WalletSettings from "../WalletSettings/WalletSettings";
|
||||
import s from "./Wallet.module.scss";
|
||||
import NavLink from "../UI/NavLink/NavLink";
|
||||
import { classNames } from "../../utils/classNames";
|
||||
|
||||
const Wallet = ({ setConnectOpened }) => {
|
||||
const Wallet = ({ setConnectOpened }: { setConnectOpened: Dispatch<SetStateAction<boolean>> }) => {
|
||||
const { state, dispatch } = useContext(Store);
|
||||
const { copied, copyToClipboard } = useCopy();
|
||||
const { censorValue } = useCensorDigits();
|
||||
|
|
@ -59,7 +60,7 @@ const Wallet = ({ setConnectOpened }) => {
|
|||
)?.unlockedBalance;
|
||||
|
||||
const flipDisplay = () => {
|
||||
updateDisplay(dispatch, !state.displayUsd);
|
||||
updateDisplay(dispatch as any, !state.displayUsd as any);
|
||||
};
|
||||
|
||||
const flipMenu = () => {
|
||||
|
|
@ -121,7 +122,7 @@ const Wallet = ({ setConnectOpened }) => {
|
|||
{getUnlockedBalance() !== state.wallet.balance && (
|
||||
<span className={s.tooltipText}>
|
||||
Locked balance:{" "}
|
||||
{(Number(state.wallet.balance) - getUnlockedBalance()).toFixed(2)}{" "}
|
||||
{(Number(state.wallet.balance) - Number(getUnlockedBalance())).toFixed(2)}{" "}
|
||||
ZANO
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -14,6 +14,31 @@ import { fetchBackground, validateTokensInput } from "../../utils/utils";
|
|||
import AssetsSelect from "./ui/AssetsSelect/AssetsSelect";
|
||||
import AdditionalDetails from "./ui/AdditionalDetails/AdditionalDetails";
|
||||
|
||||
interface ResponseData {
|
||||
data?: any;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface FetchBackgroundParams {
|
||||
method: string;
|
||||
assetId: string;
|
||||
destination: string;
|
||||
amount: string | number;
|
||||
comment: string;
|
||||
decimalPoint: number;
|
||||
password?: string;
|
||||
id?: number;
|
||||
success?: boolean;
|
||||
credentials?: {
|
||||
port: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AssetProps {
|
||||
unlockedBalance: number;
|
||||
balance: number;
|
||||
}
|
||||
|
||||
const WalletSend = () => {
|
||||
const { state } = useContext(Store);
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
|
|
@ -36,19 +61,25 @@ const WalletSend = () => {
|
|||
const isSenderInfo = useCheckbox(false);
|
||||
const isReceiverInfo = useCheckbox(false);
|
||||
|
||||
const sendTransfer = (destination, amount, comment, assetId, decimalPoint) => {
|
||||
const sendTransfer = (
|
||||
destination: string,
|
||||
amount: string | number,
|
||||
comment: string,
|
||||
assetId: string,
|
||||
decimalPoint: number
|
||||
): Promise<any> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (chrome.runtime.sendMessage) {
|
||||
if (chrome.runtime.sendMessage as any) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const response = await fetchBackground({
|
||||
const response: ResponseData = await fetchBackground({
|
||||
method: "SEND_TRANSFER",
|
||||
assetId,
|
||||
destination,
|
||||
amount,
|
||||
comment,
|
||||
decimalPoint,
|
||||
});
|
||||
} as FetchBackgroundParams);
|
||||
|
||||
if (response.data) {
|
||||
resolve(response.data);
|
||||
|
|
@ -63,7 +94,7 @@ const WalletSend = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const openExplorer = (txId) => {
|
||||
const openExplorer = (txId: string) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.tabs.create({
|
||||
url: `https://testnet-explorer.zano.org/block/${txId}`,
|
||||
|
|
@ -72,8 +103,8 @@ const WalletSend = () => {
|
|||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (address.value.startsWith("@")) {
|
||||
const alias = address.value.slice(1);
|
||||
if (String(address.value).startsWith("@")) {
|
||||
const alias = String(address.value).slice(1);
|
||||
const resolvedAddress = await fetchAddress(alias);
|
||||
if (resolvedAddress) {
|
||||
setSubmitAddress(resolvedAddress);
|
||||
|
|
@ -81,8 +112,8 @@ const WalletSend = () => {
|
|||
setSubmitAddress("");
|
||||
}
|
||||
} else {
|
||||
if (address.value.length === 97) {
|
||||
setSubmitAddress(address.value);
|
||||
if (String(address.value).length === 97) {
|
||||
setSubmitAddress(String(address.value));
|
||||
} else {
|
||||
setSubmitAddress("");
|
||||
}
|
||||
|
|
@ -91,20 +122,20 @@ const WalletSend = () => {
|
|||
}, [address.value]);
|
||||
|
||||
useEffect(() => {
|
||||
const isValid = validateTokensInput(amount.value, asset.decimalPoint);
|
||||
const isValid = !!validateTokensInput(amount.value, Number(asset.decimalPoint));
|
||||
setAmountValid(isValid);
|
||||
}, [amount.value, asset]);
|
||||
|
||||
const fetchAddress = async (alias) => await fetchBackground({ method: "GET_ALIAS_DETAILS", alias });
|
||||
const fetchAddress = async (alias: string) => await fetchBackground({ method: "GET_ALIAS_DETAILS", alias });
|
||||
|
||||
const checkAvailableBalance = (amount, asset) =>
|
||||
const checkAvailableBalance = (amount: string | number, asset: AssetProps) =>
|
||||
asset.unlockedBalance !== asset.balance
|
||||
? +amount <= asset.unlockedBalance - fee.value
|
||||
? +amount <= asset.unlockedBalance - Number(fee.value)
|
||||
: true;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------
|
||||
// Subcomponents
|
||||
const TableRow = ({ label, value }) => {
|
||||
const TableRow = ({ label, value }: { label: string; value: string }) => {
|
||||
return (
|
||||
<div className="table__row">
|
||||
<div className="table__label">{label}:</div>
|
||||
|
|
@ -113,7 +144,7 @@ const WalletSend = () => {
|
|||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{(() => {
|
||||
|
|
@ -131,21 +162,21 @@ const WalletSend = () => {
|
|||
<MyInput
|
||||
placeholder="Address or alias"
|
||||
label="Address"
|
||||
inputData={address}
|
||||
inputData={address as any}
|
||||
isValid={!!submitAddress}
|
||||
/>
|
||||
<AssetsSelect value={asset} setValue={setAsset} />
|
||||
<AssetsSelect value={asset} setValue={setAsset as React.Dispatch<React.SetStateAction<any>>} />
|
||||
<MyInput
|
||||
type="number"
|
||||
placeholder="Amount to transfer"
|
||||
label="Amount:"
|
||||
inputData={amount}
|
||||
isError={amount.value && !amountValid}
|
||||
inputData={amount as any}
|
||||
isError={amount.value ? !amountValid : false}
|
||||
/>
|
||||
<MyInput
|
||||
placeholder="Enter the comment"
|
||||
label="Comment:"
|
||||
inputData={comment}
|
||||
inputData={comment as any}
|
||||
/>
|
||||
<AdditionalDetails
|
||||
mixin={mixin}
|
||||
|
|
@ -159,7 +190,7 @@ const WalletSend = () => {
|
|||
!submitAddress ||
|
||||
!amount.value ||
|
||||
!amountValid ||
|
||||
!checkAvailableBalance(amount.value, asset)
|
||||
!checkAvailableBalance(amount.value, asset as any)
|
||||
}
|
||||
>
|
||||
Send
|
||||
|
|
@ -179,9 +210,9 @@ const WalletSend = () => {
|
|||
value={amount?.value + " " + asset?.ticker}
|
||||
/>
|
||||
<TableRow label="From" value={state?.wallet?.address} />
|
||||
<TableRow label="To" value={address.value} />
|
||||
<TableRow label="Comment" value={comment?.value} />
|
||||
<TableRow label="Fee" value={fee?.value} />
|
||||
<TableRow label="To" value={String(address.value)} />
|
||||
<TableRow label="Comment" value={String(comment?.value)} />
|
||||
<TableRow label="Fee" value={String(fee?.value)} />
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
|
@ -189,9 +220,9 @@ const WalletSend = () => {
|
|||
const transferStatus = await sendTransfer(
|
||||
submitAddress,
|
||||
amount.value,
|
||||
comment.value,
|
||||
asset.assetId,
|
||||
asset.decimalPoint
|
||||
String(comment.value),
|
||||
String(asset.assetId),
|
||||
Number(asset.decimalPoint)
|
||||
);
|
||||
console.log("transfer status", transferStatus);
|
||||
if (transferStatus.result) {
|
||||
|
|
@ -5,7 +5,39 @@ import s from "./AdditionalDetails.module.scss";
|
|||
import { classNames } from "../../../../utils/classNames";
|
||||
import arrowIcon from "../../../../assets/svg/arrow-select.svg";
|
||||
|
||||
const AdditionalDetails = ({ fee, mixin, isSenderInfo, isReceiverInfo }) => {
|
||||
interface mixinType {
|
||||
isEmpty: boolean;
|
||||
minLengthError: boolean;
|
||||
amountCorrectError: boolean;
|
||||
inputValid: boolean;
|
||||
value: string | number;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onInput: (newValue: string | number) => void;
|
||||
onBlur: () => void;
|
||||
isFilled: boolean;
|
||||
isDirty: boolean;
|
||||
}
|
||||
|
||||
interface feeType {
|
||||
isEmpty: boolean;
|
||||
minLengthError: boolean;
|
||||
amountCorrectError: boolean;
|
||||
inputValid: boolean;
|
||||
value: string | number;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onInput: (newValue: string | number) => void;
|
||||
onBlur: () => void;
|
||||
isFilled: boolean;
|
||||
isDirty: boolean;
|
||||
}
|
||||
|
||||
interface AdditionalDetailsProps {
|
||||
fee: string | number | feeType;
|
||||
mixin: string | number | mixinType;
|
||||
isSenderInfo: { isChecked: boolean; onChange: () => void };
|
||||
isReceiverInfo: { isChecked: boolean; onChange: () => void };
|
||||
}
|
||||
const AdditionalDetails = ({ fee, mixin, isSenderInfo, isReceiverInfo }: AdditionalDetailsProps) => {
|
||||
const [detailsVisible, setDetailsVisible] = useState(false);
|
||||
|
||||
const toggleDetails = () => {
|
||||
|
|
@ -27,8 +59,8 @@ const AdditionalDetails = ({ fee, mixin, isSenderInfo, isReceiverInfo }) => {
|
|||
{detailsVisible && (
|
||||
<div className={s.detailsSelectContent}>
|
||||
<div className={s.detailsSelectInputs}>
|
||||
<MyInput label="Mixin:" inputData={mixin} disabled />
|
||||
<MyInput label="Fee:" inputData={fee} disabled />
|
||||
<MyInput label="Mixin:" inputData={mixin as any} disabled />
|
||||
<MyInput label="Fee:" inputData={fee as any} disabled />
|
||||
</div>
|
||||
<div className={s.detailsSelectCheckboxes}>
|
||||
<MyCheckbox
|
||||
|
|
@ -9,13 +9,22 @@ import mainStyles from "../../WalletSend.module.scss";
|
|||
import s from "./AssetsSelect.module.scss";
|
||||
import { classNames } from "../../../../utils/classNames";
|
||||
|
||||
const AssetsSelect = ({ value, setValue }) => {
|
||||
interface Asset {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface AssetsSelectProps {
|
||||
value: Asset;
|
||||
setValue: (asset: Asset) => void;
|
||||
}
|
||||
|
||||
const AssetsSelect = ({ value, setValue }: AssetsSelectProps) => {
|
||||
const { state } = useContext(Store);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [focusedIndex, setFocusedIndex] = React.useState(null);
|
||||
const selectRef = useRef(null);
|
||||
const [focusedIndex, setFocusedIndex] = React.useState<number | null>(null);
|
||||
const selectRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
if (
|
||||
|
|
@ -24,21 +33,24 @@ const AssetsSelect = ({ value, setValue }) => {
|
|||
) {
|
||||
setFocusedIndex(0);
|
||||
} else {
|
||||
setFocusedIndex((prevIndex) => prevIndex + 1);
|
||||
setFocusedIndex((prevIndex) => Number(prevIndex) + 1);
|
||||
}
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
if (focusedIndex === null || focusedIndex === 0) {
|
||||
setFocusedIndex(state.wallet.assets.length - 1);
|
||||
} else {
|
||||
setFocusedIndex((prevIndex) => prevIndex - 1);
|
||||
setFocusedIndex((prevIndex) => Number(prevIndex) - 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (focusedIndex !== null && selectRef.current) {
|
||||
selectRef.current.childNodes[focusedIndex].focus();
|
||||
const childNodes = selectRef.current.childNodes;
|
||||
if (childNodes && childNodes[focusedIndex as number]) {
|
||||
(childNodes[focusedIndex as number] as HTMLElement).focus();
|
||||
}
|
||||
}
|
||||
}, [focusedIndex]);
|
||||
|
||||
|
|
@ -46,12 +58,12 @@ const AssetsSelect = ({ value, setValue }) => {
|
|||
setIsOpen(!isOpen);
|
||||
}
|
||||
|
||||
function setValueHandler(asset) {
|
||||
function setValueHandler(asset: Asset) {
|
||||
setValue(asset);
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
const getAssetImage = (name) => {
|
||||
const getAssetImage = (name: string) => {
|
||||
switch (name) {
|
||||
case "Zano":
|
||||
return zanoIcon;
|
||||
|
|
@ -7,7 +7,7 @@ import s from "./WalletSettings.module.scss";
|
|||
|
||||
const WalletSettings = () => {
|
||||
const [isBtnDisabled, setIsBtnDisabled] = useState(true);
|
||||
const localNodePort = useInput("11112");
|
||||
const localNodePort = useInput("11112", {});
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
|
||||
const useAwayClick = (ref, callback) => {
|
||||
const savedCallback = useRef();
|
||||
type Callback = () => void;
|
||||
|
||||
const useAwayClick = (ref: React.RefObject<HTMLElement>, callback: Callback) => {
|
||||
const savedCallback = useRef<Callback | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = (event) => {
|
||||
if (ref.current && !ref.current.contains(event.target)) {
|
||||
savedCallback.current();
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||||
savedCallback.current?.();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -6,10 +6,10 @@ export function useCensorDigits() {
|
|||
const { state, dispatch } = useContext(Store);
|
||||
|
||||
const changeCensor = () => {
|
||||
updateBalancesHidden(dispatch, (prevState) => !prevState);
|
||||
updateBalancesHidden(dispatch, (prevState: boolean) => !prevState);
|
||||
};
|
||||
|
||||
const censorValue = (number) => {
|
||||
const censorValue = (number: number | string): string | number => {
|
||||
if (state.isBalancesHidden) {
|
||||
return number.toString().replace(/\d/g, "*");
|
||||
} else {
|
||||
|
|
@ -19,3 +19,4 @@ export function useCensorDigits() {
|
|||
|
||||
return { changeCensor, censorValue };
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState } from "react";
|
||||
|
||||
export const useCheckbox = (initialState) => {
|
||||
export const useCheckbox = (initialState: boolean) => {
|
||||
const [isChecked, setIsChecked] = useState(initialState);
|
||||
|
||||
const onChange = () => {
|
||||
|
|
@ -4,7 +4,7 @@ import { useState } from "react";
|
|||
export const useCopy = () => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = (text) => {
|
||||
const copyToClipboard = (text: string) => {
|
||||
copy(text);
|
||||
if (!copied) {
|
||||
setCopied(true);
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import { useContext } from "react";
|
||||
import { Store } from "../store/store-reducer";
|
||||
|
||||
|
||||
export default function useGetAsset() {
|
||||
const {state} = useContext(Store);
|
||||
|
||||
function getAssetById(id) {
|
||||
return state.wallet.assets.find(asset => asset.assetId === id);
|
||||
}
|
||||
|
||||
return { getAssetById };
|
||||
}
|
||||
16
src/app/hooks/useGetAsset.ts
Normal file
16
src/app/hooks/useGetAsset.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { useContext } from "react";
|
||||
import { Store } from "../store/store-reducer";
|
||||
|
||||
interface Asset {
|
||||
assetId: string;
|
||||
}
|
||||
|
||||
export default function useGetAsset() {
|
||||
const { state } = useContext(Store);
|
||||
|
||||
function getAssetById(id: string) {
|
||||
return state.wallet.assets.find((asset: Asset | any) => asset.assetId === id);
|
||||
}
|
||||
|
||||
return { getAssetById };
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { useValidation } from "./useValidation";
|
||||
|
||||
export const useInput = (initialState, validations) => {
|
||||
const [value, setValue] = useState(initialState);
|
||||
const [isDirty, setIsDirty] = useState(false);
|
||||
const valid = useValidation(value, validations);
|
||||
|
||||
const onChange = (e) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
|
||||
const onInput = (value) => {
|
||||
setValue(value);
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
setIsDirty(true);
|
||||
};
|
||||
|
||||
const isFilled = value.length > 0;
|
||||
|
||||
return { value, onChange, onInput, onBlur, isFilled, isDirty, ...valid };
|
||||
};
|
||||
34
src/app/hooks/useInput.ts
Normal file
34
src/app/hooks/useInput.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { useState } from "react";
|
||||
import { useValidation } from "./useValidation";
|
||||
|
||||
type Validations = {
|
||||
minLength?: number;
|
||||
isEmpty?: boolean;
|
||||
isAmountCorrect?: boolean;
|
||||
customValidation?: boolean;
|
||||
};
|
||||
|
||||
export const useInput = (
|
||||
initialState: string | number,
|
||||
validations: Validations
|
||||
) => {
|
||||
const [value, setValue] = useState<string | number>(initialState);
|
||||
const [isDirty, setIsDirty] = useState<boolean>(false);
|
||||
const valid = useValidation(value, validations);
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
|
||||
const onInput = (newValue: string | number) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
setIsDirty(true);
|
||||
};
|
||||
|
||||
const isFilled = typeof value === "string" && value.length > 0;
|
||||
|
||||
return { value, onChange, onInput, onBlur, isFilled, isDirty, ...valid };
|
||||
};
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
export const useValidation = (value, validations) => {
|
||||
const [isEmpty, setIsEmpty] = useState(true);
|
||||
const [minLengthError, setMinLengthError] = useState(false);
|
||||
const [amountCorrectError, setAmountCorrectError] = useState(false);
|
||||
const [inputValid, setInputValid] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
for (const validation in validations) {
|
||||
switch (validation) {
|
||||
case "minLength":
|
||||
value.length < validations[validation]
|
||||
? setMinLengthError(true)
|
||||
: setMinLengthError(false);
|
||||
break;
|
||||
case "isEmpty":
|
||||
value ? setIsEmpty(false) : setIsEmpty(true);
|
||||
break;
|
||||
case "isAmountCorrect":
|
||||
const amountCheckResult =
|
||||
!isNaN(value) && value >= 0.000000000001 && value <= 1000000000;
|
||||
setAmountCorrectError(!amountCheckResult);
|
||||
break;
|
||||
case "customValidation":
|
||||
setInputValid(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [validations, value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmpty || minLengthError || amountCorrectError) {
|
||||
setInputValid(false);
|
||||
} else {
|
||||
setInputValid(true);
|
||||
}
|
||||
}, [isEmpty, minLengthError, amountCorrectError]);
|
||||
|
||||
return {
|
||||
isEmpty,
|
||||
minLengthError,
|
||||
amountCorrectError,
|
||||
inputValid,
|
||||
};
|
||||
};
|
||||
60
src/app/hooks/useValidation.ts
Normal file
60
src/app/hooks/useValidation.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
type Validations = {
|
||||
minLength?: number;
|
||||
isEmpty?: boolean;
|
||||
isAmountCorrect?: boolean;
|
||||
customValidation?: boolean;
|
||||
};
|
||||
|
||||
export const useValidation = (value: string | number, validations: Validations) => {
|
||||
const [isEmpty, setIsEmpty] = useState<boolean>(true);
|
||||
const [minLengthError, setMinLengthError] = useState<boolean>(false);
|
||||
const [amountCorrectError, setAmountCorrectError] = useState<boolean>(false);
|
||||
const [inputValid, setInputValid] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
for (const validation in validations) {
|
||||
switch (validation) {
|
||||
case "minLength":
|
||||
if (typeof value === "string" && value.length < validations[validation]!) {
|
||||
setMinLengthError(true);
|
||||
} else {
|
||||
setMinLengthError(false);
|
||||
}
|
||||
break;
|
||||
case "isEmpty":
|
||||
setIsEmpty(!value);
|
||||
break;
|
||||
case "isAmountCorrect":
|
||||
const amountCheckResult =
|
||||
typeof value === "number" &&
|
||||
!isNaN(value) &&
|
||||
value >= 0.000000000001 &&
|
||||
value <= 1000000000;
|
||||
setAmountCorrectError(!amountCheckResult);
|
||||
break;
|
||||
case "customValidation":
|
||||
setInputValid(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [validations, value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmpty || minLengthError || amountCorrectError) {
|
||||
setInputValid(false);
|
||||
} else {
|
||||
setInputValid(true);
|
||||
}
|
||||
}, [isEmpty, minLengthError, amountCorrectError]);
|
||||
|
||||
return {
|
||||
isEmpty,
|
||||
minLengthError,
|
||||
amountCorrectError,
|
||||
inputValid,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
export const updateWalletsList = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "WALLETS_LIST_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateWalletData = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "WALLET_DATA_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateWalletConnected = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "WALLET_CONNECTED_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateActiveWalletId = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "ACTIVE_WALLET_ID_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updatePriceData = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "PRICE_DATA_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDisplay = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "DISPLAY_CURRENCY_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateLoading = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "LOADING_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateBalancesHidden = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "BALANCES_HIDDEN_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateConfirmationModal = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "CONFIRMATION_MODAL_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateTransactionStatus = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "TRANSACTION_STATUS_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const setConnectData = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "SET_CONNECT_DATA",
|
||||
payload: state
|
||||
});
|
||||
};
|
||||
|
||||
export const setWhiteList = (dispatch, state) => {
|
||||
return dispatch({
|
||||
type: "SET_WHITE_LIST",
|
||||
payload: state
|
||||
});
|
||||
}
|
||||
107
src/app/store/actions.ts
Normal file
107
src/app/store/actions.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Define types for the structure of your state
|
||||
interface WalletState {
|
||||
wallets: string[];
|
||||
walletData: object;
|
||||
isConnected: boolean;
|
||||
activeWalletId: string | number;
|
||||
priceData: object;
|
||||
displayCurrency: string;
|
||||
isLoading: boolean;
|
||||
balancesHidden: boolean;
|
||||
confirmationModalOpen: boolean | null | {method: string; params: string[]};
|
||||
transactionStatus: string;
|
||||
connectData: object;
|
||||
whiteList: string[];
|
||||
}
|
||||
|
||||
type DispatchFunction = (action: { type: string; payload: WalletState[keyof WalletState] }) => void;
|
||||
|
||||
export const updateWalletsList = (dispatch: DispatchFunction, state: WalletState['wallets']): void => {
|
||||
return dispatch({
|
||||
type: "WALLETS_LIST_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateWalletData = (dispatch: DispatchFunction, state: WalletState['walletData']): void => {
|
||||
return dispatch({
|
||||
type: "WALLET_DATA_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateWalletConnected = (dispatch: DispatchFunction, state: WalletState['isConnected']): void => {
|
||||
return dispatch({
|
||||
type: "WALLET_CONNECTED_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateActiveWalletId = (dispatch: DispatchFunction, state: WalletState['activeWalletId']): void => {
|
||||
return dispatch({
|
||||
type: "ACTIVE_WALLET_ID_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updatePriceData = (dispatch: DispatchFunction, state: WalletState['priceData']): void => {
|
||||
return dispatch({
|
||||
type: "PRICE_DATA_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDisplay = (dispatch: DispatchFunction, state: WalletState['displayCurrency']): void => {
|
||||
return dispatch({
|
||||
type: "DISPLAY_CURRENCY_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateLoading = (dispatch: DispatchFunction, state: WalletState['isLoading']): void => {
|
||||
return dispatch({
|
||||
type: "LOADING_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateBalancesHidden = (dispatch: any, state: any): void => {
|
||||
return dispatch({
|
||||
type: "BALANCES_HIDDEN_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateConfirmationModal = (dispatch: DispatchFunction, state: WalletState['confirmationModalOpen']): void => {
|
||||
return dispatch({
|
||||
type: "CONFIRMATION_MODAL_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
export const updateTransactionStatus = (dispatch: DispatchFunction, state: WalletState['transactionStatus'] | any): void => {
|
||||
return dispatch({
|
||||
type: "TRANSACTION_STATUS_UPDATED",
|
||||
payload: state,
|
||||
});
|
||||
};
|
||||
|
||||
interface ConnectCredentials {
|
||||
token: string;
|
||||
port: string;
|
||||
}
|
||||
|
||||
|
||||
export const setConnectData = (dispatch: DispatchFunction, state: ConnectCredentials): void => {
|
||||
return dispatch({
|
||||
type: "SET_CONNECT_DATA",
|
||||
payload: state
|
||||
});
|
||||
};
|
||||
|
||||
export const setWhiteList = (dispatch: DispatchFunction, state: WalletState['whiteList']): void => {
|
||||
return dispatch({
|
||||
type: "SET_WHITE_LIST",
|
||||
payload: state
|
||||
});
|
||||
};
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
import { createContext, useReducer } from "react";
|
||||
|
||||
const initialState = {
|
||||
walletsList: [
|
||||
{
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
alias: "ravaga",
|
||||
balance: 1337,
|
||||
},
|
||||
{
|
||||
address:
|
||||
"ZxDCEeVaHsYXEJotN8Q5y4PW7Y4inrNaibqpmU7P9KGCZ76LBPYkn9Gf8BzCSLSJfpVDJ7GzBPApGEK4BVbogZwN2opPAQDfU",
|
||||
alias: "test",
|
||||
balance: 27,
|
||||
},
|
||||
],
|
||||
activeWalletId: 0,
|
||||
wallet: {
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
alias: "ravaga",
|
||||
balance: 1337,
|
||||
lockedBalance: 0,
|
||||
assets: [
|
||||
{
|
||||
name: "Zano",
|
||||
ticker: "ZANO",
|
||||
balance: 1337,
|
||||
lockedBalance: 0,
|
||||
value: 1000,
|
||||
},
|
||||
{
|
||||
name: "Wrapped Bitcoin",
|
||||
ticker: "WBTC",
|
||||
balance: 0.212,
|
||||
value: 4096.96,
|
||||
},
|
||||
{
|
||||
name: "Wrapped Ethereum",
|
||||
ticker: "WETH",
|
||||
balance: 2.1,
|
||||
value: 3020.12,
|
||||
},
|
||||
{
|
||||
name: "Confidential Token",
|
||||
ticker: "CT",
|
||||
balance: 15.52,
|
||||
value: 672.84,
|
||||
},
|
||||
],
|
||||
transactions: [
|
||||
{
|
||||
isConfirmed: true,
|
||||
incoming: true,
|
||||
amount: 100,
|
||||
ticker: "ZANO",
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
},
|
||||
{
|
||||
isConfirmed: false,
|
||||
incoming: false,
|
||||
value: 17,
|
||||
ticker: "ZANO",
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
},
|
||||
],
|
||||
},
|
||||
displayUsd: false,
|
||||
isLoading: true,
|
||||
isConnected: undefined,
|
||||
isBalancesHidden: false,
|
||||
priceData: { price: 1, change: -4.6 },
|
||||
confirmationModal: null,
|
||||
transactionStatus: {
|
||||
visible: false,
|
||||
type: "",
|
||||
code: 0,
|
||||
message: "",
|
||||
},
|
||||
connectCredentials: {
|
||||
token: null,
|
||||
port: null,
|
||||
},
|
||||
whitelistedAssets: [],
|
||||
};
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "WALLET_ADDRESS_UPDATED":
|
||||
return { ...state, walletAddress: action.payload };
|
||||
case "WALLET_BALANCE_UPDATED":
|
||||
return { ...state, walletBalance: action.payload };
|
||||
case "WALLET_CONNECTED_UPDATED":
|
||||
return { ...state, isConnected: action.payload };
|
||||
case "WALLETS_LIST_UPDATED":
|
||||
return { ...state, walletsList: action.payload };
|
||||
case "ACTIVE_WALLET_ID_UPDATED":
|
||||
return { ...state, activeWalletId: action.payload };
|
||||
case "WALLET_DATA_UPDATED":
|
||||
return { ...state, wallet: action.payload };
|
||||
case "PRICE_DATA_UPDATED":
|
||||
return { ...state, priceData: action.payload };
|
||||
case "DISPLAY_CURRENCY_UPDATED":
|
||||
return { ...state, displayUsd: action.payload };
|
||||
case "LOADING_UPDATED":
|
||||
return { ...state, isLoading: action.payload };
|
||||
case "BALANCES_HIDDEN_UPDATED":
|
||||
return { ...state, isBalancesHidden: action.payload };
|
||||
case "CONFIRMATION_MODAL_UPDATED":
|
||||
return { ...state, confirmationModal: action.payload };
|
||||
case "TRANSACTION_STATUS_UPDATED":
|
||||
return { ...state, transactionStatus: action.payload };
|
||||
case "SET_CONNECT_DATA":
|
||||
return { ...state, connectCredentials: action.payload }
|
||||
case "SET_WHITE_LIST":
|
||||
return { ...state, whitelistedAssets: action.payload }
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const Store = createContext(initialState);
|
||||
|
||||
export const StoreProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
return (
|
||||
<Store.Provider value={{ state, dispatch }}>{children}</Store.Provider>
|
||||
);
|
||||
};
|
||||
253
src/app/store/store-reducer.tsx
Normal file
253
src/app/store/store-reducer.tsx
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
import React, { createContext, useReducer, ReactNode, useContext } from "react";
|
||||
|
||||
// Define the types for the state
|
||||
interface Asset {
|
||||
name: string;
|
||||
ticker: string;
|
||||
balance: number;
|
||||
lockedBalance?: number;
|
||||
unlockedBalance?: number;
|
||||
value: number;
|
||||
decimalPoint?: number;
|
||||
assetId?: string;
|
||||
}
|
||||
|
||||
interface Transfer {
|
||||
assetId?: string;
|
||||
amount?: number;
|
||||
incoming?: boolean;
|
||||
}
|
||||
|
||||
interface Transaction {
|
||||
txHash?: string;
|
||||
isConfirmed: boolean;
|
||||
incoming: boolean;
|
||||
amount?: number;
|
||||
value?: number;
|
||||
ticker: string;
|
||||
address: string;
|
||||
transfers?: Transfer[];
|
||||
isInitiator?: boolean;
|
||||
fee?: number | string;
|
||||
}
|
||||
|
||||
interface Wallet {
|
||||
address: string;
|
||||
alias: string;
|
||||
balance: number;
|
||||
lockedBalance?: number;
|
||||
assets: Asset[];
|
||||
transactions: Transaction[];
|
||||
}
|
||||
|
||||
interface TransactionStatus {
|
||||
visible: boolean;
|
||||
type: string;
|
||||
code: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ConnectCredentials {
|
||||
token: string | null;
|
||||
port: string | null;
|
||||
}
|
||||
|
||||
interface PriceData {
|
||||
price: number;
|
||||
change: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
walletsList: { address: string; alias: string; balance: number, wallet_id?: number; }[];
|
||||
activeWalletId: number;
|
||||
wallet: Wallet;
|
||||
displayUsd: boolean;
|
||||
isLoading: boolean;
|
||||
isConnected: boolean | undefined;
|
||||
isBalancesHidden: boolean;
|
||||
priceData: PriceData;
|
||||
confirmationModal: string | null | any;
|
||||
transactionStatus: TransactionStatus;
|
||||
connectCredentials: ConnectCredentials;
|
||||
whitelistedAssets: string[];
|
||||
walletAddress?: string;
|
||||
walletBalance?: number;
|
||||
}
|
||||
|
||||
// Initial state
|
||||
const initialState: State = {
|
||||
walletsList: [
|
||||
{
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
alias: "ravaga",
|
||||
balance: 1337,
|
||||
},
|
||||
{
|
||||
address:
|
||||
"ZxDCEeVaHsYXEJotN8Q5y4PW7Y4inrNaibqpmU7P9KGCZ76LBPYkn9Gf8BzCSLSJfpVDJ7GzBPApGEK4BVbogZwN2opPAQDfU",
|
||||
alias: "test",
|
||||
balance: 27,
|
||||
},
|
||||
],
|
||||
activeWalletId: 0,
|
||||
wallet: {
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
alias: "ravaga",
|
||||
balance: 1337,
|
||||
lockedBalance: 0,
|
||||
assets: [
|
||||
{
|
||||
name: "Zano",
|
||||
ticker: "ZANO",
|
||||
balance: 1337,
|
||||
lockedBalance: 0,
|
||||
value: 1000,
|
||||
},
|
||||
{
|
||||
name: "Wrapped Bitcoin",
|
||||
ticker: "WBTC",
|
||||
balance: 0.212,
|
||||
value: 4096.96,
|
||||
},
|
||||
{
|
||||
name: "Wrapped Ethereum",
|
||||
ticker: "WETH",
|
||||
balance: 2.1,
|
||||
value: 3020.12,
|
||||
},
|
||||
{
|
||||
name: "Confidential Token",
|
||||
ticker: "CT",
|
||||
balance: 15.52,
|
||||
value: 672.84,
|
||||
},
|
||||
],
|
||||
transactions: [
|
||||
{
|
||||
isConfirmed: true,
|
||||
incoming: true,
|
||||
amount: 100,
|
||||
ticker: "ZANO",
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
},
|
||||
{
|
||||
isConfirmed: false,
|
||||
incoming: false,
|
||||
value: 17,
|
||||
ticker: "ZANO",
|
||||
address:
|
||||
"ZxDTZ8LJ88ZK6Ja1P9iqDNgCiBM6FhiBKdDoTAoEp9nY9q8d846iePAGYGjNvrU9uFHDXD3by5CooSBrsXBDfE9M11WBwAxQ9",
|
||||
},
|
||||
],
|
||||
},
|
||||
displayUsd: false,
|
||||
isLoading: true,
|
||||
isConnected: undefined,
|
||||
isBalancesHidden: false,
|
||||
priceData: { price: 1, change: -4.6 },
|
||||
confirmationModal: null,
|
||||
transactionStatus: {
|
||||
visible: false,
|
||||
type: "",
|
||||
code: 0,
|
||||
message: "",
|
||||
},
|
||||
connectCredentials: {
|
||||
token: null,
|
||||
port: null,
|
||||
},
|
||||
whitelistedAssets: [],
|
||||
};
|
||||
|
||||
type Action =
|
||||
| { type: "WALLET_ADDRESS_UPDATED"; payload: string }
|
||||
| { type: "WALLET_BALANCE_UPDATED"; payload: number }
|
||||
| { type: "WALLET_CONNECTED_UPDATED"; payload: boolean | undefined }
|
||||
| { type: "WALLETS_LIST_UPDATED"; payload: { address: string; alias: string; balance: number }[] }
|
||||
| { type: "ACTIVE_WALLET_ID_UPDATED"; payload: number }
|
||||
| { type: "WALLET_DATA_UPDATED"; payload: Wallet }
|
||||
| { type: "PRICE_DATA_UPDATED"; payload: PriceData }
|
||||
| { type: "DISPLAY_CURRENCY_UPDATED"; payload: boolean }
|
||||
| { type: "LOADING_UPDATED"; payload: boolean }
|
||||
| { type: "BALANCES_HIDDEN_UPDATED"; payload: boolean }
|
||||
| { type: "CONFIRMATION_MODAL_UPDATED"; payload: string | null }
|
||||
| { type: "TRANSACTION_STATUS_UPDATED"; payload: TransactionStatus }
|
||||
| { type: "SET_CONNECT_DATA"; payload: ConnectCredentials }
|
||||
| { type: "SET_WHITE_LIST"; payload: string[] }
|
||||
| { type: "SET_BALANCES_HIDDEN"; payload: boolean };
|
||||
|
||||
const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "WALLET_ADDRESS_UPDATED":
|
||||
return { ...state, walletAddress: action.payload };
|
||||
case "WALLET_BALANCE_UPDATED":
|
||||
return { ...state, walletBalance: action.payload };
|
||||
case "WALLET_CONNECTED_UPDATED":
|
||||
return { ...state, isConnected: action.payload };
|
||||
case "WALLETS_LIST_UPDATED":
|
||||
return { ...state, walletsList: action.payload };
|
||||
case "ACTIVE_WALLET_ID_UPDATED":
|
||||
return { ...state, activeWalletId: action.payload };
|
||||
case "WALLET_DATA_UPDATED":
|
||||
return { ...state, wallet: action.payload };
|
||||
case "PRICE_DATA_UPDATED":
|
||||
return { ...state, priceData: action.payload };
|
||||
case "DISPLAY_CURRENCY_UPDATED":
|
||||
return { ...state, displayUsd: action.payload };
|
||||
case "LOADING_UPDATED":
|
||||
return { ...state, isLoading: action.payload };
|
||||
case "BALANCES_HIDDEN_UPDATED":
|
||||
return { ...state, isBalancesHidden: action.payload };
|
||||
case "CONFIRMATION_MODAL_UPDATED":
|
||||
return { ...state, confirmationModal: action.payload };
|
||||
case "TRANSACTION_STATUS_UPDATED":
|
||||
return { ...state, transactionStatus: action.payload };
|
||||
case "SET_CONNECT_DATA":
|
||||
return { ...state, connectCredentials: action.payload };
|
||||
case "SET_WHITE_LIST":
|
||||
return { ...state, whitelistedAssets: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const Store = createContext<{ state: State; dispatch: React.Dispatch<Action> }>({
|
||||
state: initialState,
|
||||
dispatch: () => { },
|
||||
});
|
||||
|
||||
interface StoreProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const StoreProvider = ({ children }: StoreProviderProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
return <Store.Provider value={{ state, dispatch }}>{children}</Store.Provider>;
|
||||
};
|
||||
|
||||
export const useStore = () => useContext(Store);
|
||||
|
||||
const updateWhiteList = (dispatch: React.Dispatch<Action>, whiteList: string[]) => {
|
||||
dispatch({
|
||||
type: "SET_WHITE_LIST",
|
||||
payload: whiteList,
|
||||
});
|
||||
};
|
||||
|
||||
// Usage in a component:
|
||||
const ExampleComponent = () => {
|
||||
const { state, dispatch } = useStore();
|
||||
|
||||
const handleUpdateWhiteList = () => {
|
||||
updateWhiteList(dispatch, ["asset1", "asset2"]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={handleUpdateWhiteList}>Update WhiteList</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import CryptoJS from "crypto-js";
|
||||
|
||||
export default class ConnectKeyUtils {
|
||||
static setConnectData(key, walletPort, extPass) {
|
||||
const data = JSON.stringify({ token: key, port: walletPort });
|
||||
localStorage.setItem("connectKey", CryptoJS.AES.encrypt(data, extPass).toString());
|
||||
}
|
||||
|
||||
static getConnectKeyEncrypted() {
|
||||
return localStorage.getItem("connectKey");
|
||||
}
|
||||
|
||||
static getConnectData(password) {
|
||||
const encrypted = localStorage.getItem("connectKey");
|
||||
const decrypted = CryptoJS.AES.decrypt(encrypted, password).toString(CryptoJS.enc.Utf8);
|
||||
const data = JSON.parse(decrypted);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
34
src/app/utils/ConnectKeyUtils.ts
Normal file
34
src/app/utils/ConnectKeyUtils.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import CryptoJS from "crypto-js";
|
||||
|
||||
interface ConnectData {
|
||||
token: string;
|
||||
port: string;
|
||||
}
|
||||
|
||||
export default class ConnectKeyUtils {
|
||||
static setConnectData(key: string, walletPort: string, extPass: string): void {
|
||||
const data: ConnectData = { token: key, port: walletPort };
|
||||
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), extPass).toString();
|
||||
localStorage.setItem("connectKey", encrypted);
|
||||
}
|
||||
|
||||
static getConnectKeyEncrypted(): string | null {
|
||||
return localStorage.getItem("connectKey");
|
||||
}
|
||||
|
||||
static getConnectData(password: string): ConnectData | null {
|
||||
const encrypted = localStorage.getItem("connectKey");
|
||||
if (!encrypted) return null;
|
||||
|
||||
const decrypted = CryptoJS.AES.decrypt(encrypted, password).toString(CryptoJS.enc.Utf8);
|
||||
|
||||
if (!decrypted) return null;
|
||||
|
||||
try {
|
||||
const data: ConnectData = JSON.parse(decrypted);
|
||||
return data;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
export const classNames = (cls, mods, additional = []) =>
|
||||
[
|
||||
cls,
|
||||
...additional.filter(Boolean),
|
||||
...Object.entries(mods)
|
||||
.filter(([className, value]) => Boolean(value))
|
||||
.map(([classNames]) => classNames),
|
||||
].join(" ");
|
||||
15
src/app/utils/classNames.ts
Normal file
15
src/app/utils/classNames.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
type Mods = Record<string, boolean | string | undefined>;
|
||||
|
||||
export const classNames = (
|
||||
cls: string,
|
||||
mods: Mods,
|
||||
additional: (string | undefined)[] = []
|
||||
): string => {
|
||||
return [
|
||||
cls,
|
||||
...additional.filter(Boolean),
|
||||
...Object.entries(mods)
|
||||
.filter(([className, value]) => Boolean(value))
|
||||
.map(([classNames]) => classNames),
|
||||
].join(" ");
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
export default class Formatters {
|
||||
static walletAddress(str) {
|
||||
static walletAddress(str: string): string {
|
||||
if (str.length > 20) {
|
||||
if (window.innerWidth > 768) {
|
||||
return (
|
||||
|
|
@ -18,7 +18,7 @@ export default class Formatters {
|
|||
return str;
|
||||
}
|
||||
|
||||
static historyAmount(amount) {
|
||||
static historyAmount(amount: number): string {
|
||||
let str = amount.toString();
|
||||
if (str.length > 10) {
|
||||
return (
|
||||
|
|
@ -3,7 +3,23 @@ import Big from "big.js";
|
|||
import Decimal from "decimal.js";
|
||||
import sha256 from "sha256";
|
||||
|
||||
export async function fetchBackground(data) {
|
||||
interface BackgroundResponse {
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface ValidationResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export async function fetchBackground(data: {
|
||||
method: string;
|
||||
password?: string;
|
||||
id?: number;
|
||||
success?: boolean;
|
||||
credentials?: { port: string };
|
||||
alias?: string;
|
||||
}): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
chrome.runtime.sendMessage(data, function (response) {
|
||||
|
|
@ -16,45 +32,43 @@ export async function fetchBackground(data) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
export const removeZeros = (amount, decimal_point = 12) => {
|
||||
export const removeZeros = (amount: string | number, decimal_point: number = 12): string => {
|
||||
const multiplier = new Big(10).pow(decimal_point);
|
||||
const bigAmount = new Big(amount);
|
||||
const fixedAmount = bigAmount.div(multiplier).toString();
|
||||
return fixedAmount;
|
||||
};
|
||||
|
||||
export const addZeros = (amount, decimal_point = 12) => {
|
||||
export const addZeros = (amount: string | number, decimal_point: number = 12): Big => {
|
||||
const multiplier = new Big(10).pow(decimal_point);
|
||||
const bigAmount = new Big(amount);
|
||||
const fixedAmount = bigAmount.times(multiplier);
|
||||
return fixedAmount;
|
||||
};
|
||||
|
||||
export const setPassword = (password) => {
|
||||
export const setPassword = (password: string): void => {
|
||||
localStorage.setItem("hash", sha256(password));
|
||||
}
|
||||
};
|
||||
|
||||
export const comparePasswords = (password) => {
|
||||
export const comparePasswords = (password: string): boolean => {
|
||||
const hash = localStorage.getItem("hash");
|
||||
return hash === sha256(password);
|
||||
}
|
||||
};
|
||||
|
||||
export const passwordExists = () => {
|
||||
export const passwordExists = (): boolean => {
|
||||
return !!localStorage.getItem("hash");
|
||||
}
|
||||
};
|
||||
|
||||
export const getSessionPassword = async () => {
|
||||
const sessionPass = (await fetchBackground({ method: "GET_PASSWORD" })).password;
|
||||
return sessionPass;
|
||||
}
|
||||
export const getSessionPassword = async (): Promise<string> => {
|
||||
const sessionPass = (await fetchBackground({ method: "GET_PASSWORD" })) as BackgroundResponse;
|
||||
return sessionPass.password;
|
||||
};
|
||||
|
||||
export const setSessionPassword = async (password) => {
|
||||
await fetchBackground({ method: "SET_PASSWORD", password })
|
||||
}
|
||||
|
||||
export function validateTokensInput(input, decimal_point) {
|
||||
export const setSessionPassword = async (password: string): Promise<void> => {
|
||||
await fetchBackground({ method: "SET_PASSWORD", password });
|
||||
};
|
||||
|
||||
export function validateTokensInput(input: string | number, decimal_point: number): ValidationResult {
|
||||
if (typeof input === 'number') {
|
||||
input = input.toString();
|
||||
}
|
||||
|
|
@ -129,4 +143,4 @@ export function validateTokensInput(input, decimal_point) {
|
|||
return {
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -20,9 +20,17 @@ import JSONbig from "json-bigint";
|
|||
const POPUP_HEIGHT = 630;
|
||||
const POPUP_WIDTH = 370;
|
||||
|
||||
const ZANO_ID = "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a";
|
||||
const ZANO_ID =
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a";
|
||||
|
||||
async function getAsset(assetId) {
|
||||
interface Asset {
|
||||
asset_id: string;
|
||||
ticker: string;
|
||||
full_name: string;
|
||||
decimal_point: number;
|
||||
}
|
||||
|
||||
async function getAsset(assetId: string): Promise<Asset | undefined> {
|
||||
if (assetId === ZANO_ID) {
|
||||
return {
|
||||
asset_id: ZANO_ID,
|
||||
|
|
@ -42,19 +50,42 @@ async function getAsset(assetId) {
|
|||
}
|
||||
}
|
||||
|
||||
interface PopupRequest {
|
||||
windowId: number;
|
||||
finalizer: (data: unknown) => void;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface RequestResponse {
|
||||
error?: string;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
interface ErrorMessages {
|
||||
console: string;
|
||||
response: string;
|
||||
reqNotFound: string;
|
||||
}
|
||||
|
||||
class PopupRequestsMethods {
|
||||
static onRequestCreate(requestType, request, sendResponse, reqParams) {
|
||||
static onRequestCreate(
|
||||
requestType: keyof typeof savedRequests,
|
||||
request: { timeout?: number },
|
||||
sendResponse: (response: RequestResponse) => void,
|
||||
reqParams: PopupRequest
|
||||
): void {
|
||||
console.log("Creating request", reqParams);
|
||||
|
||||
|
||||
openWindow().then((requestWindow) => {
|
||||
const reqId = crypto.randomUUID();
|
||||
const req = {
|
||||
windowId: requestWindow.id,
|
||||
finalizer: (data) => sendResponse(data),
|
||||
...reqParams,
|
||||
windowId: requestWindow.id,
|
||||
finalizer: (data: unknown) => sendResponse(data as any),
|
||||
};
|
||||
allPopupIds.push(requestWindow.id);
|
||||
savedRequests[requestType][reqId] = req;
|
||||
|
||||
allPopupIds.push(requestWindow.id as number);
|
||||
(savedRequests[requestType][reqId] as any) = req;
|
||||
|
||||
if (typeof request.timeout === "number") {
|
||||
setTimeout(() => {
|
||||
|
|
@ -65,7 +96,10 @@ class PopupRequestsMethods {
|
|||
});
|
||||
}
|
||||
|
||||
static getRequestsList(requestType, sendResponse) {
|
||||
static getRequestsList(
|
||||
requestType: keyof typeof savedRequests,
|
||||
sendResponse: (response: { data: PopupRequest[] }) => void
|
||||
): void {
|
||||
sendResponse({
|
||||
data: Object.entries(savedRequests[requestType]).map(([id, req]) => ({
|
||||
...req,
|
||||
|
|
@ -76,18 +110,18 @@ class PopupRequestsMethods {
|
|||
}
|
||||
|
||||
static onRequestFinalize(
|
||||
requestType,
|
||||
request,
|
||||
sendResponse,
|
||||
apiCallFunc,
|
||||
errorMessages
|
||||
) {
|
||||
requestType: keyof typeof savedRequests,
|
||||
request: { id: string; success: boolean },
|
||||
sendResponse: (response: RequestResponse) => void,
|
||||
apiCallFunc: (req: PopupRequest) => Promise<unknown>,
|
||||
errorMessages: ErrorMessages
|
||||
): void {
|
||||
const reqId = request.id;
|
||||
const success = request.success;
|
||||
const req = savedRequests[requestType][reqId];
|
||||
|
||||
if (req) {
|
||||
function finalize(data) {
|
||||
function finalize(data: unknown) {
|
||||
req.finalizer(data);
|
||||
delete savedRequests[requestType][reqId];
|
||||
chrome.windows.remove(req.windowId);
|
||||
|
|
@ -109,18 +143,18 @@ class PopupRequestsMethods {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
return sendResponse({ error: errorMessages.reqNotFound });
|
||||
sendResponse({ error: errorMessages.reqNotFound });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chrome.windows.onBoundsChanged.addListener((window) => {
|
||||
if (
|
||||
allPopupIds.includes(window.id) &&
|
||||
allPopupIds.includes(window.id as number) &&
|
||||
window.width !== POPUP_WIDTH &&
|
||||
window.height !== POPUP_HEIGHT
|
||||
) {
|
||||
chrome.windows.update(window.id, {
|
||||
chrome.windows.update(window.id as number, {
|
||||
width: POPUP_WIDTH,
|
||||
height: POPUP_HEIGHT,
|
||||
});
|
||||
|
|
@ -132,23 +166,40 @@ chrome.runtime.onStartup.addListener(() => {
|
|||
console.log("Background script loaded on startup");
|
||||
});
|
||||
|
||||
const defaultCredentials = {
|
||||
interface Credentials {
|
||||
port: number;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
const defaultCredentials: Credentials = {
|
||||
port: 11211,
|
||||
};
|
||||
|
||||
export let apiCredentials = JSON.parse(JSON.stringify(defaultCredentials));
|
||||
export let apiCredentials: Credentials = { ...defaultCredentials };
|
||||
|
||||
let pendingTx = null;
|
||||
interface pendingTxTypes {
|
||||
assetId: string,
|
||||
amount: string,
|
||||
destinationAddress: string | undefined,
|
||||
destinationChainId: string | undefined,
|
||||
}
|
||||
|
||||
const defaultUserData = { password: undefined };
|
||||
let pendingTx: pendingTxTypes | null = null;
|
||||
|
||||
async function setUserData(state) {
|
||||
interface UserData {
|
||||
password?: string;
|
||||
apiCredentials?: Credentials;
|
||||
}
|
||||
|
||||
const defaultUserData: UserData = { password: undefined };
|
||||
|
||||
async function setUserData(state: UserData): Promise<void> {
|
||||
await new Promise((resolve) => {
|
||||
chrome.storage.local.set({ userData: state }, resolve);
|
||||
chrome.storage.local.set({ userData: state }, resolve as (() => void));
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserData() {
|
||||
async function getUserData(): Promise<UserData> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get("userData", (result) => {
|
||||
resolve(result.userData || defaultUserData);
|
||||
|
|
@ -156,41 +207,47 @@ async function getUserData() {
|
|||
});
|
||||
}
|
||||
|
||||
async function updateUserData(newData) {
|
||||
async function updateUserData(newData: Partial<UserData>): Promise<void> {
|
||||
const currentData = await getUserData();
|
||||
return setUserData({ ...currentData, ...newData });
|
||||
}
|
||||
|
||||
async function recoverApiCredentials() {
|
||||
async function recoverApiCredentials(): Promise<void> {
|
||||
apiCredentials = (await getUserData()).apiCredentials || defaultCredentials;
|
||||
}
|
||||
|
||||
chrome.runtime.onStartup.addListener(() => {
|
||||
chrome.storage.local.remove("userData", function () {
|
||||
chrome.storage.local.remove("userData", () => {
|
||||
console.log("State cleared on browser startup");
|
||||
});
|
||||
});
|
||||
|
||||
const signReqFinalizers = {};
|
||||
const signReqs = [];
|
||||
interface SignReqFinalizer {
|
||||
[key: string]: (data: unknown) => void;
|
||||
}
|
||||
|
||||
const savedRequests = {
|
||||
const signReqFinalizers: SignReqFinalizer = {};
|
||||
const signReqs: unknown[] = [];
|
||||
|
||||
const savedRequests: Record<
|
||||
"IONIC_SWAP" | "ACCEPT_IONIC_SWAP" | "CREATE_ALIAS" | "TRANSFER",
|
||||
Record<string, PopupRequest>
|
||||
> = {
|
||||
IONIC_SWAP: {},
|
||||
ACCEPT_IONIC_SWAP: {},
|
||||
CREATE_ALIAS: {},
|
||||
TRANSFER: {}
|
||||
TRANSFER: {},
|
||||
};
|
||||
|
||||
const allPopupIds = [];
|
||||
const allPopupIds: number[] = [];
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.storage.local.get("pendingTx", (result) => {
|
||||
if (result.pendingTx) {
|
||||
pendingTx = result.pendingTx;
|
||||
}
|
||||
});
|
||||
|
||||
function openWindow() {
|
||||
function openWindow(): Promise<chrome.windows.Window> {
|
||||
return chrome.windows.create({
|
||||
url: chrome.runtime.getURL("index.html"),
|
||||
type: "popup",
|
||||
|
|
@ -218,15 +275,60 @@ const SELF_ONLY_REQUESTS = [
|
|||
"PING_WALLET",
|
||||
"SET_ACTIVE_WALLET",
|
||||
"GET_WALLETS",
|
||||
"FINALIZE_TRANSFER_REQUEST"
|
||||
"FINALIZE_TRANSFER_REQUEST",
|
||||
];
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
processRequest(request, sender, sendResponse);
|
||||
processRequest(request, sender as any, sendResponse);
|
||||
return true;
|
||||
});
|
||||
|
||||
async function processRequest(request, sender, sendResponse) {
|
||||
interface RequestType {
|
||||
method: string;
|
||||
credentials: Object;
|
||||
id: string;
|
||||
assetId: string;
|
||||
destination: string;
|
||||
amount: string;
|
||||
decimalPoint: string;
|
||||
success: boolean;
|
||||
destinationAssetID: string;
|
||||
currentAssetID: string;
|
||||
currentAsset: Asset;
|
||||
destinationAsset: Asset;
|
||||
hex_raw_proposal?: string;
|
||||
alias?: string;
|
||||
sender?: string;
|
||||
transfer?: any;
|
||||
swapProposal?: any;
|
||||
password?: string;
|
||||
key?: string;
|
||||
aliasDetails?: any;
|
||||
signReqs?: any[];
|
||||
windowId?: number;
|
||||
message?: string;
|
||||
timeout?: number;
|
||||
destinationChainId?: string;
|
||||
destinationAddress?: string;
|
||||
receivingAsset?: any;
|
||||
sendingAsset?: any;
|
||||
asset?: Asset;
|
||||
}
|
||||
|
||||
interface Sender {
|
||||
id: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
address?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface SendResponse {
|
||||
(response: any): void;
|
||||
}
|
||||
|
||||
async function processRequest(request: RequestType, sender: Sender, sendResponse: SendResponse) {
|
||||
const isFromExtensionFrontend =
|
||||
sender.url && sender.url.includes(chrome.runtime.getURL("/"));
|
||||
|
||||
|
|
@ -300,7 +402,7 @@ async function processRequest(request, sender, sendResponse) {
|
|||
break;
|
||||
|
||||
case "GET_WALLET_DATA":
|
||||
getWalletData(request.id)
|
||||
getWalletData() // removed request.id
|
||||
.then((data) => {
|
||||
sendResponse({ data });
|
||||
})
|
||||
|
|
@ -371,14 +473,14 @@ async function processRequest(request, sender, sendResponse) {
|
|||
"IONIC_SWAP",
|
||||
request,
|
||||
sendResponse,
|
||||
{ swap: request }
|
||||
{ swap: request } as any
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "GET_TRANSFER_REQUEST": {
|
||||
PopupRequestsMethods.getRequestsList("TRANSFER", sendResponse);
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
case "TRANSFER": {
|
||||
|
|
@ -386,39 +488,45 @@ async function processRequest(request, sender, sendResponse) {
|
|||
const asset = await getAsset(request.assetId);
|
||||
const walletData = await getWalletData();
|
||||
const { address } = walletData;
|
||||
request.asset = asset || await getAsset(ZANO_ID);
|
||||
request.sender = address || '';
|
||||
|
||||
} catch (e) {
|
||||
return sendResponse({error: e.message})
|
||||
request.asset = asset || (await getAsset(ZANO_ID));
|
||||
request.sender = address || "";
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
return sendResponse({ error: e.message });
|
||||
} else {
|
||||
return sendResponse({ error: 'Unknown error occurred' });
|
||||
}
|
||||
}
|
||||
|
||||
PopupRequestsMethods.onRequestCreate(
|
||||
"TRANSFER",
|
||||
request,
|
||||
sendResponse,
|
||||
{ transfer: request }
|
||||
);
|
||||
break
|
||||
PopupRequestsMethods.onRequestCreate("TRANSFER", request, sendResponse, {
|
||||
transfer: request,
|
||||
} as any);
|
||||
break;
|
||||
}
|
||||
|
||||
case "FINALIZE_TRANSFER_REQUEST": {
|
||||
PopupRequestsMethods
|
||||
.onRequestFinalize("TRANSFER",
|
||||
request,
|
||||
sendResponse,
|
||||
(req) => {
|
||||
const transferData = req.transfer;
|
||||
const {assetId, destination, amount, asset } = transferData;
|
||||
|
||||
return transfer(assetId, destination, amount, asset?.decimal_point || 12);
|
||||
},
|
||||
{
|
||||
console: "Error transfer:",
|
||||
response: "An error occurred while sending transfer",
|
||||
reqNotFound: "transfer request not found",
|
||||
})
|
||||
break
|
||||
PopupRequestsMethods.onRequestFinalize(
|
||||
"TRANSFER",
|
||||
request,
|
||||
sendResponse,
|
||||
(req) => {
|
||||
const transferData: any = req.transfer;
|
||||
const { assetId, destination, amount, asset } = transferData;
|
||||
|
||||
return transfer(
|
||||
assetId,
|
||||
destination,
|
||||
amount,
|
||||
asset?.decimal_point || 12
|
||||
);
|
||||
},
|
||||
{
|
||||
console: "Error transfer:",
|
||||
response: "An error occurred while sending transfer",
|
||||
reqNotFound: "transfer request not found",
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "GET_ACCEPT_IONIC_SWAP_REQUESTS":
|
||||
|
|
@ -499,7 +607,7 @@ async function processRequest(request, sender, sendResponse) {
|
|||
"ACCEPT_IONIC_SWAP",
|
||||
request,
|
||||
sendResponse,
|
||||
request
|
||||
request as any
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
@ -568,7 +676,7 @@ async function processRequest(request, sender, sendResponse) {
|
|||
}
|
||||
|
||||
case "GET_ALIAS_DETAILS": {
|
||||
getAliasDetails(request.alias)
|
||||
getAliasDetails(String(request.alias))
|
||||
.then((res) => sendResponse(res))
|
||||
.catch(() => sendResponse({ error: "Internal error" }));
|
||||
break;
|
||||
|
|
@ -582,10 +690,10 @@ async function processRequest(request, sender, sendResponse) {
|
|||
case "FINALIZE_MESSAGE_SIGN": {
|
||||
const reqId = request.id;
|
||||
const success = request.success;
|
||||
const signReq = signReqs.find((req) => req.id === reqId);
|
||||
const signReq: any = signReqs.find((req: any) => req.id === reqId);
|
||||
|
||||
if (signReq && signReqFinalizers[reqId]) {
|
||||
function finalize(data) {
|
||||
function finalize(data: any) {
|
||||
signReqFinalizers[reqId](data);
|
||||
signReqs.splice(signReqs.indexOf(signReq), 1);
|
||||
delete signReqFinalizers[reqId];
|
||||
|
|
@ -630,7 +738,7 @@ async function processRequest(request, sender, sendResponse) {
|
|||
sendResponse(result);
|
||||
};
|
||||
|
||||
allPopupIds.push(requestWindow.id);
|
||||
allPopupIds.push(Number(requestWindow.id));
|
||||
|
||||
signReqs.push({
|
||||
id: signReqId,
|
||||
|
|
@ -641,7 +749,7 @@ async function processRequest(request, sender, sendResponse) {
|
|||
if (typeof request.timeout === "number") {
|
||||
setTimeout(() => {
|
||||
const signReqIndex = signReqs.findIndex(
|
||||
(req) => req.id === signReqId
|
||||
(req: any) => req.id === signReqId
|
||||
);
|
||||
|
||||
if (signReqIndex === -1) {
|
||||
|
|
@ -712,7 +820,7 @@ async function processRequest(request, sender, sendResponse) {
|
|||
"CREATE_ALIAS",
|
||||
request,
|
||||
sendResponse,
|
||||
{ alias: request.alias }
|
||||
({ alias: request.alias } as any)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
@ -721,4 +829,4 @@ async function processRequest(request, sender, sendResponse) {
|
|||
console.error("Unknown message method:", request.method);
|
||||
sendResponse({ error: `Unknown method: ${request.method}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,14 @@ import { Buffer } from "buffer";
|
|||
import JSONbig from "json-bigint";
|
||||
// window.Buffer = Buffer;
|
||||
|
||||
function createJWSToken(payload, secrete_str) {
|
||||
interface JWTPayload {
|
||||
body_hash: string,
|
||||
user: string,
|
||||
salt: string,
|
||||
exp: number
|
||||
}
|
||||
|
||||
function createJWSToken(payload: JWTPayload, secretStr: string): string {
|
||||
const header = { alg: "HS256", typ: "JWT" };
|
||||
const encodedHeader = Buffer.from(JSON.stringify(header))
|
||||
.toString("base64")
|
||||
|
|
@ -15,7 +22,7 @@ function createJWSToken(payload, secrete_str) {
|
|||
.replace(/=/g, "");
|
||||
|
||||
const signature = forge.hmac.create();
|
||||
signature.start("sha256", secrete_str);
|
||||
signature.start("sha256", secretStr);
|
||||
signature.update(`${encodedHeader}.${encodedPayload}`);
|
||||
const encodedSignature = forge.util
|
||||
.encode64(signature.digest().getBytes())
|
||||
|
|
@ -24,13 +31,13 @@ function createJWSToken(payload, secrete_str) {
|
|||
return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
|
||||
}
|
||||
|
||||
function generateRandomString(length) {
|
||||
function generateRandomString(length: number) {
|
||||
const bytes = forge.random.getBytesSync(Math.ceil(length / 2));
|
||||
const hexString = forge.util.bytesToHex(bytes);
|
||||
return hexString.substring(0, length);
|
||||
}
|
||||
|
||||
function generateAccessToken(httpBody) {
|
||||
function generateAccessToken(httpBody: string) {
|
||||
if (!apiCredentials?.token) {
|
||||
throw new Error("No API credentials found, extension is not connected");
|
||||
}
|
||||
|
|
@ -51,16 +58,23 @@ function generateAccessToken(httpBody) {
|
|||
return createJWSToken(payload, apiCredentials?.token);
|
||||
}
|
||||
|
||||
export const fetchData = async (method, params = {}) => {
|
||||
const httpBody = JSON.stringify({
|
||||
interface fetchDataProps {
|
||||
offset: number,
|
||||
update_provision_info: boolean,
|
||||
exclude_mining_txs: boolean,
|
||||
count: number,
|
||||
order: string,
|
||||
exclude_unconfirmed: boolean,
|
||||
}
|
||||
|
||||
export const fetchData = async (method: string, params: fetchDataProps | {} = {}): Promise<Response> => {
|
||||
const httpBody: string = JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: "0",
|
||||
method,
|
||||
params,
|
||||
});
|
||||
|
||||
// console.log("fetchData:", httpBody);
|
||||
|
||||
|
||||
return fetch(`http://localhost:${apiCredentials.port}/json_rpc`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
|
@ -93,7 +107,7 @@ const fetchTxData = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getAlias = async (address) => {
|
||||
export const getAlias = async (address:string) => {
|
||||
const response = await fetchData("get_alias_by_address", address);
|
||||
const data = await response.json();
|
||||
if (data.result?.status === "OK") {
|
||||
|
|
@ -103,7 +117,7 @@ export const getAlias = async (address) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getAliasDetails = async (alias) => {
|
||||
export const getAliasDetails = async (alias: string) => {
|
||||
const response = await fetchData("get_alias_details", { alias });
|
||||
const data = await response.json();
|
||||
if (data.result.status === "OK") {
|
||||
|
|
@ -125,10 +139,10 @@ export const getWallets = async () => {
|
|||
// console.log("wallets:", data.result.wallets);
|
||||
|
||||
const wallets = await Promise.all(
|
||||
data.result.wallets.map(async (wallet) => {
|
||||
data.result.wallets.map(async (wallet: any) => {
|
||||
const alias = await getAlias(wallet.wi.address);
|
||||
const balance = wallet.wi.balances.find(
|
||||
(asset) =>
|
||||
(asset: any) =>
|
||||
asset.asset_info.asset_id ===
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
).total;
|
||||
|
|
@ -163,7 +177,7 @@ export const getWalletData = async () => {
|
|||
const balanceParsed = JSONbig.parse(await balanceResponse.text());
|
||||
|
||||
const assets = balanceParsed.result.balances
|
||||
.map((asset) => ({
|
||||
.map((asset: any) => ({
|
||||
name: asset.asset_info.full_name,
|
||||
ticker: asset.asset_info.ticker,
|
||||
assetId: asset.asset_info.asset_id,
|
||||
|
|
@ -174,7 +188,7 @@ export const getWalletData = async () => {
|
|||
asset.asset_info.decimal_point
|
||||
),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
.sort((a: any, b:any) => {
|
||||
if (
|
||||
a.assetId ===
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
|
|
@ -189,13 +203,13 @@ export const getWalletData = async () => {
|
|||
return 0;
|
||||
});
|
||||
|
||||
function getAssetDecimalPoint(assetId) {
|
||||
return assets.find((asset) => asset.assetId === assetId)?.decimalPoint;
|
||||
function getAssetDecimalPoint(assetId: any) {
|
||||
return assets.find((asset:any) => asset.assetId === assetId)?.decimalPoint;
|
||||
}
|
||||
|
||||
const balance = removeZeros(
|
||||
balanceParsed.result.balances.find(
|
||||
(asset) =>
|
||||
(asset: any) =>
|
||||
asset.asset_info.asset_id ===
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
).total
|
||||
|
|
@ -206,8 +220,8 @@ export const getWalletData = async () => {
|
|||
|
||||
if (txData) {
|
||||
transactions = txData
|
||||
.filter((tx) => !tx.is_service)
|
||||
.map((tx) => ({
|
||||
.filter((tx:any) => !tx.is_service)
|
||||
.map((tx:any) => ({
|
||||
isConfirmed: tx.height === 0 ? false : true,
|
||||
txHash: tx.tx_hash,
|
||||
blobSize: tx.tx_blob_size,
|
||||
|
|
@ -218,9 +232,9 @@ export const getWalletData = async () => {
|
|||
fee: removeZeros(tx.fee),
|
||||
addresses: tx.remote_addresses,
|
||||
isInitiator: !!tx.employed_entries?.spent?.some?.(
|
||||
(e) => e?.index === 0
|
||||
(e:any) => e?.index === 0
|
||||
),
|
||||
transfers: tx.subtransfers.map((transfer) => ({
|
||||
transfers: tx.subtransfers.map((transfer:any) => ({
|
||||
amount: removeZeros(
|
||||
transfer.amount,
|
||||
getAssetDecimalPoint(transfer.asset_id) || 12
|
||||
|
|
@ -237,7 +251,7 @@ export const getWalletData = async () => {
|
|||
return { address, alias, balance, transactions, assets };
|
||||
};
|
||||
|
||||
export const ionicSwap = async (swapParams) => {
|
||||
export const ionicSwap = async (swapParams:any) => {
|
||||
const response = await fetchData("ionic_swap_generate_proposal", {
|
||||
proposal: {
|
||||
to_initiator: [
|
||||
|
|
@ -273,7 +287,7 @@ export const ionicSwap = async (swapParams) => {
|
|||
return data;
|
||||
};
|
||||
|
||||
export const ionicSwapAccept = async (swapParams) => {
|
||||
export const ionicSwapAccept = async (swapParams:any) => {
|
||||
console.log(swapParams.hex_raw_proposal);
|
||||
|
||||
const response = await fetchData("ionic_swap_accept_proposal", {
|
||||
|
|
@ -288,7 +302,7 @@ export const ionicSwapAccept = async (swapParams) => {
|
|||
return data;
|
||||
};
|
||||
|
||||
export const createAlias = async ({ alias, address }) => {
|
||||
export const createAlias = async ({ alias, address }: any) => {
|
||||
const response = await fetchData("register_alias", {
|
||||
al: {
|
||||
address: address,
|
||||
|
|
@ -301,9 +315,9 @@ export const createAlias = async ({ alias, address }) => {
|
|||
|
||||
export const transfer = async (
|
||||
assetId = "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a",
|
||||
destination,
|
||||
amount,
|
||||
decimalPoint
|
||||
destination: any,
|
||||
amount: any,
|
||||
decimalPoint: any
|
||||
) => {
|
||||
const destinations = [
|
||||
{
|
||||
|
|
@ -333,9 +347,9 @@ export const transfer = async (
|
|||
// TODO: move bridge address to the config
|
||||
export const burnBridge = async (
|
||||
assetId = "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a",
|
||||
amount,
|
||||
destinationAddress,
|
||||
destinationChainId
|
||||
amount: any,
|
||||
destinationAddress: any,
|
||||
destinationChainId: any
|
||||
) => {
|
||||
const bodyData = {
|
||||
service_id: "B",
|
||||
|
|
@ -381,7 +395,7 @@ export const burnBridge = async (
|
|||
return data;
|
||||
};
|
||||
|
||||
export const signMessage = async (message) => {
|
||||
export const signMessage = async (message: any) => {
|
||||
const base64 = Buffer.from(message).toString("base64");
|
||||
|
||||
const signRequest = {
|
||||
|
|
@ -409,7 +423,7 @@ export const createConnectKey = async () => {
|
|||
).then((r) => r.json());
|
||||
};
|
||||
|
||||
export const validateConnectKey = async (key) => {
|
||||
export const validateConnectKey = async (key: any) => {
|
||||
return await fetch(
|
||||
`http://localhost:${apiCredentials.port}/validate-connection-key`,
|
||||
{
|
||||
|
|
@ -422,7 +436,7 @@ export const validateConnectKey = async (key) => {
|
|||
).then((r) => r.json());
|
||||
};
|
||||
|
||||
export const getSwapProposalInfo = async (hex) => {
|
||||
export const getSwapProposalInfo = async (hex: any) => {
|
||||
const response = await fetchData("ionic_swap_get_proposal_info", {
|
||||
hex_raw_proposal: hex,
|
||||
});
|
||||
|
|
@ -445,7 +459,7 @@ export async function getWhiteList() {
|
|||
|
||||
if (
|
||||
fetchedWhiteList.every(
|
||||
(e) =>
|
||||
(e:any) =>
|
||||
e.asset_id !==
|
||||
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"
|
||||
)
|
||||
|
|
@ -462,7 +476,7 @@ export async function getWhiteList() {
|
|||
return fetchedWhiteList;
|
||||
}
|
||||
|
||||
export async function getAssetInfo(assetId) {
|
||||
export async function getAssetInfo(assetId: any) {
|
||||
const response = await fetchData("get_asset_info", { asset_id: assetId });
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
@ -1,11 +1,24 @@
|
|||
async function fetchData(data) {
|
||||
interface DocumentEventMap {
|
||||
"zano_request": CustomEvent<ZanoRequestData>;
|
||||
}
|
||||
|
||||
interface ZanoRequestData {
|
||||
method: string;
|
||||
listenerID: string;
|
||||
timeout?: number | null;
|
||||
[key: string]: string | number | boolean | null | undefined;
|
||||
}
|
||||
|
||||
interface ZanoResponse {
|
||||
error?: string;
|
||||
[key: string]: string | number | boolean | null | undefined;
|
||||
}
|
||||
|
||||
async function fetchData(data: ZanoRequestData): Promise<ZanoResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.runtime.sendMessage(data, (response) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.runtime.sendMessage(data, (response: ZanoResponse) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
// eslint-disable-next-line no-undef
|
||||
reject(chrome.runtime.lastError);
|
||||
} else {
|
||||
resolve(response);
|
||||
|
|
@ -18,7 +31,7 @@ async function fetchData(data) {
|
|||
});
|
||||
}
|
||||
|
||||
document.addEventListener("zano_request", async (e) => {
|
||||
document.addEventListener("zano_request", async (e: CustomEvent<ZanoRequestData>) => {
|
||||
const data = e.detail;
|
||||
|
||||
try {
|
||||
|
|
@ -31,10 +44,9 @@ document.addEventListener("zano_request", async (e) => {
|
|||
);
|
||||
} catch (error) {
|
||||
console.error(`Error while processing zano_request:`, error);
|
||||
// Dispatch an event with the error
|
||||
document.dispatchEvent(
|
||||
new CustomEvent(`zano_response_${data.listenerID}`, {
|
||||
detail: { error: error.message },
|
||||
detail: { error: error instanceof Error ? error.message : String(error) },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
class Zano {
|
||||
async request(method, params, timeoutParam) {
|
||||
async request(method: string, params: Record<string, any>, timeoutParam?: number): Promise<any> {
|
||||
|
||||
function getRandonString(length) {
|
||||
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
let charLength = chars.length;
|
||||
function getRandonString(length: number): string {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
const charLength = chars.length;
|
||||
let result = '';
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
|
|
@ -13,28 +13,27 @@ class Zano {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
const listenerID = getRandonString(16);
|
||||
const timeoutMs = typeof timeoutParam === "number" ? timeoutParam : null;
|
||||
const timeoutMs: number | null = typeof timeoutParam === "number" ? timeoutParam : null;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const timeout = timeoutMs !== null ? (
|
||||
setTimeout(() => {
|
||||
reject('Request timeout exceeded');
|
||||
document.removeEventListener(`zano_response_${listenerID}`, handleResponse);
|
||||
document.removeEventListener(`zano_response_${listenerID}`, handleResponse as EventListener);
|
||||
}, timeoutMs)
|
||||
) : undefined;
|
||||
|
||||
function handleResponse(e) {
|
||||
document.removeEventListener(`zano_response_${listenerID}`, handleResponse);
|
||||
function handleResponse(e: CustomEvent) {
|
||||
document.removeEventListener(`zano_response_${listenerID}`, handleResponse as EventListener);
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
resolve(e.detail);
|
||||
}
|
||||
|
||||
document.addEventListener(`zano_response_${listenerID}`, handleResponse);
|
||||
document.addEventListener(`zano_response_${listenerID}`, handleResponse as EventListener);
|
||||
|
||||
document.dispatchEvent(new CustomEvent('zano_request', {
|
||||
detail: {
|
||||
|
|
@ -49,4 +48,4 @@ class Zano {
|
|||
}
|
||||
}
|
||||
|
||||
window.zano = new Zano();
|
||||
window.zano = new Zano();
|
||||
29
src/global.d.ts
vendored
Normal file
29
src/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
interface Window {
|
||||
zano: Zano;
|
||||
}
|
||||
|
||||
declare module "*.scss" {
|
||||
const content: { [className: string]: string };
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.svg" {
|
||||
import React from "react";
|
||||
const content: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.png" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module "*.jpg" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module "*.jpeg" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client";
|
|||
import App from "./app/App";
|
||||
import { StoreProvider } from "./app/store/store-reducer";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<StoreProvider>
|
||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "es6",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"outDir": "./dist",
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -54,9 +54,14 @@ module.exports = {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: "ts-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ["*", ".js", ".jsx"],
|
||||
extensions: ["*", ".js", ".jsx", ".ts", ".tsx"],
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const HtmlWebpackPlugin = require("html-webpack-plugin");
|
|||
const common = require("./webpack.common.js");
|
||||
|
||||
const appConfig = {
|
||||
entry: "./src/index.js",
|
||||
entry: "./src/index.tsx",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build"),
|
||||
filename: "static/js/app.bundle.js",
|
||||
|
|
@ -20,7 +20,7 @@ const appConfig = {
|
|||
};
|
||||
|
||||
const backgroundConfig = {
|
||||
entry: "./src/background/background.js",
|
||||
entry: "./src/background/background.ts",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build/static/js"),
|
||||
filename: "background.bundle.js",
|
||||
|
|
@ -28,7 +28,7 @@ const backgroundConfig = {
|
|||
};
|
||||
|
||||
const contentConfig = {
|
||||
entry: "./src/content/content.js",
|
||||
entry: "./src/content/content.ts",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build/static/js"),
|
||||
filename: "content.bundle.js",
|
||||
|
|
@ -36,7 +36,7 @@ const contentConfig = {
|
|||
};
|
||||
|
||||
const injectConfig = {
|
||||
entry: "./src/content/inject.js",
|
||||
entry: "./src/content/inject.ts",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build/static/js"),
|
||||
filename: "inject.bundle.js",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue