Skip to content

Commit

Permalink
Merge pull request #468 from starknet-id/feat/starknet_react_v2
Browse files Browse the repository at this point in the history
feat: use starknet-react v2 & add custom transaction manager
  • Loading branch information
fricoben authored Nov 7, 2023
2 parents 6fff33a + 55f8b8b commit fc5158e
Show file tree
Hide file tree
Showing 25 changed files with 1,823 additions and 1,372 deletions.
17 changes: 14 additions & 3 deletions components/UI/modalProfilePic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import ClickableAction from "./iconsComponents/clickableAction";
import theme from "../../styles/theme";
import DoneFilledIcon from "./iconsComponents/icons/doneFilledIcon";
import ArrowLeftIcon from "./iconsComponents/icons/arrowLeftIcon";
import { useContractWrite, useTransactionManager } from "@starknet-react/core";
import { useContractWrite } from "@starknet-react/core";
import { Call } from "starknet";
import identityChangeCalls from "../../utils/callData/identityChangeCalls";
import { hexToDecimal, toUint256 } from "../../utils/feltService";
import { getImgUrl } from "../../utils/stringService";
import { StarknetIdJsContext } from "../../context/StarknetIdJsProvider";
import { useNotificationManager } from "../../hooks/useNotificationManager";
import { NotificationType, TransactionType } from "../../utils/constants";

type ModalProfilePicProps = {
closeModal: (cancel: boolean) => void;
Expand All @@ -30,7 +32,7 @@ const ModalProfilePic: FunctionComponent<ModalProfilePicProps> = ({
tokenId,
}) => {
const [callData, setCallData] = useState<Call[]>([]);
const { addTransaction } = useTransactionManager();
const { addTransaction } = useNotificationManager();
const { writeAsync: execute, data: updateData } = useContractWrite({
calls: callData,
});
Expand All @@ -51,7 +53,16 @@ const ModalProfilePic: FunctionComponent<ModalProfilePicProps> = ({

useEffect(() => {
if (!updateData?.transaction_hash) return;
addTransaction({ hash: updateData.transaction_hash });
addTransaction({
timestamp: Date.now(),
subtext: `For identity ${tokenId}`,
type: NotificationType.TRANSACTION,
data: {
type: TransactionType.SET_PFP,
hash: updateData.transaction_hash,
status: "pending",
},
});
setPfpTxHash(updateData.transaction_hash);
updateIdentityImg(tokenId, nftData.image_url as string);
closeModal(false);
Expand Down
53 changes: 20 additions & 33 deletions components/UI/modalWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ import React, { useEffect, useState } from "react";
import styles from "../../styles/components/walletMessage.module.css";
import { FunctionComponent } from "react";
import { Modal } from "@mui/material";
import { useAccount, useTransactions } from "@starknet-react/core";
import { useAccount } from "@starknet-react/core";
import ClickableAction from "./iconsComponents/clickableAction";
import { CommonTransactionReceiptResponse } from "starknet";
import CloseIcon from "./iconsComponents/icons/closeIcon";
import ArgentIcon from "./iconsComponents/icons/argentIcon";
import theme from "../../styles/theme";
import ExitIcon from "./iconsComponents/icons/exitIcon";
import CopyIcon from "./iconsComponents/icons/copyIcon";
import DoneIcon from "./iconsComponents/icons/doneIcon";
import { useNotificationManager } from "../../hooks/useNotificationManager";

type ModalWalletProps = {
closeModal: () => void;
open: boolean;
domain: string;
disconnectByClick: () => void;
hashes: string[];
setTxLoading: (txLoading: number) => void;
};

Expand All @@ -26,36 +25,27 @@ const ModalWallet: FunctionComponent<ModalWalletProps> = ({
open,
domain,
disconnectByClick,
hashes,
setTxLoading,
}) => {
const { address, connector } = useAccount();
const [copied, setCopied] = useState(false);
const transactions = useTransactions({ hashes, watch: true });
const network =
process.env.NEXT_PUBLIC_IS_TESTNET === "true" ? "testnet" : "mainnet";
// Argent web wallet is detectable only like this
const isWebWallet = (connector as any)?._wallet?.id === "argentWebWallet";
const { notifications } = useNotificationManager();

// TODO: Check for starknet react fix and delete that code
useEffect(() => {
const interval = setInterval(() => {
for (const tx of transactions) {
tx.refetch();
}
}, 3_000);
return () => clearInterval(interval);
}, [transactions?.length]);

useEffect(() => {
if (transactions) {
// Give the number of tx that are loading (I use any because there is a problem on Starknet React types)
if (notifications) {
// Give the number of tx that are loading
setTxLoading(
transactions.filter((tx) => (tx?.data as any)?.status === "RECEIVED")
.length
notifications.filter(
(notif: SIDNotification<TransactionData>) =>
notif.data.status === "pending"
).length
);
}
}, [transactions]);
}, [notifications]);

const copyToClipboard = () => {
if (!address) return;
Expand Down Expand Up @@ -132,32 +122,29 @@ const ModalWallet: FunctionComponent<ModalWalletProps> = ({
<div className={styles.menu_txs}>
<div className={styles.tx_title}>My transactions</div>
<div>
{transactions && transactions.length > 0 ? (
transactions.map((tx) => {
{notifications && notifications.length > 0 ? (
notifications.map((tx) => {
return (
<div
className={styles.menu_tx}
key={tx.data?.transaction_hash}
>
<div className={styles.menu_tx} key={tx.data?.hash}>
<a
href={`https://${
network === "testnet" ? "testnet." : ""
}starkscan.co/tx/${tx.data?.transaction_hash}`}
}starkscan.co/tx/${tx.data?.hash}`}
className={styles.tx_hash}
target="_blank"
rel="noreferrer"
>
{tx.data?.transaction_hash?.slice(0, 6) +
{tx.data?.hash?.slice(0, 6) +
"..." +
tx.data?.transaction_hash?.slice(
tx.data?.transaction_hash.length - 6,
tx.data?.transaction_hash.length
tx.data?.hash?.slice(
tx.data?.hash.length - 6,
tx.data?.hash.length
)}
</a>
<div>
{tx.status === "success" &&
{tx.data.status === "success" &&
tx.data &&
(tx.data as CommonTransactionReceiptResponse).status}
tx.data?.txStatus}
</div>
</div>
);
Expand Down
104 changes: 43 additions & 61 deletions components/UI/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import Link from "next/link";
import React, { useState, useEffect, FunctionComponent } from "react";
import React, {
useState,
useEffect,
FunctionComponent,
useCallback,
} from "react";
import { AiOutlineClose, AiOutlineMenu } from "react-icons/ai";
import { FaDiscord, FaTwitter } from "react-icons/fa";
import styles from "../../styles/components/navbar.module.css";
import Button from "./button";
import {
useConnectors,
useConnect,
useAccount,
useProvider,
useTransactionManager,
useDisconnect,
Connector,
} from "@starknet-react/core";
import Wallets from "./wallets";
Expand All @@ -25,52 +29,36 @@ const Navbar: FunctionComponent = () => {
const theme = useTheme();
const [nav, setNav] = useState<boolean>(false);
const [hasWallet, setHasWallet] = useState<boolean>(false);
const { address } = useAccount();
const { address, account } = useAccount();
const [isConnected, setIsConnected] = useState<boolean>(false);
const [isWrongNetwork, setIsWrongNetwork] = useState(false);
const { available, connect, disconnect, refresh, connectors } =
useConnectors();
const { provider } = useProvider();
const { connect, connectors } = useConnect();
const { disconnect } = useDisconnect();
const isMobile = useMediaQuery("(max-width:425px)");
const domainOrAddress = useDisplayName(address ?? "", isMobile);
const network =
process.env.NEXT_PUBLIC_IS_TESTNET === "true" ? "testnet" : "mainnet";
const [txLoading, setTxLoading] = useState<number>(0);
const { hashes } = useTransactionManager();
const [showWallet, setShowWallet] = useState<boolean>(false);

useEffect(() => {
async function tryAutoConnect(connectors: Connector[]) {
// to handle autoconnect starknet-react adds connector id in local storage
// if there is no value stored, we show the wallet modal
const lastConnectedConnectorId =
localStorage.getItem("lastUsedConnector");
if (lastConnectedConnectorId === null) {
return;
}

const lastConnectedConnector = connectors.find(
(connector) => connector.id === lastConnectedConnectorId
);
if (lastConnectedConnector === undefined) {
return;
}

try {
if (!(await lastConnectedConnector.ready())) {
// Not authorized anymore.
return;
}

await connect(lastConnectedConnector);
} catch {
// no-op
}
}

// to handle autoconnect starknet-react adds connector id in local storage
// if there is no value stored, we show the wallet modal
const timeout = setTimeout(() => {
if (!address) {
tryAutoConnect(connectors);
if (!localStorage.getItem("lastUsedConnector")) {
if (connectors.length > 0) setHasWallet(true);
} else {
const lastConnectedConnectorId =
localStorage.getItem("lastUsedConnector");
if (lastConnectedConnectorId === null) return;

const lastConnectedConnector = connectors.find(
(connector) => connector.id === lastConnectedConnectorId
);
if (lastConnectedConnector === undefined) return;
tryConnect(lastConnectedConnector);
}
}
}, 1000);
return () => clearTimeout(timeout);
Expand All @@ -81,17 +69,28 @@ const Navbar: FunctionComponent = () => {
}, [address]);

useEffect(() => {
if (!isConnected) return;

provider.getChainId().then((chainId) => {
if (!isConnected || !account) return;
account.getChainId().then((chainId) => {
const isWrongNetwork =
(chainId === constants.StarknetChainId.SN_GOERLI &&
network === "mainnet") ||
(chainId === constants.StarknetChainId.SN_MAIN &&
network === "testnet");
setIsWrongNetwork(isWrongNetwork);
});
}, [provider, network, isConnected]);
}, [account, network, isConnected]);

const tryConnect = useCallback(
async (connector: Connector) => {
if (address) return;
if (await connector.ready()) {
connect({ connector });

return;
}
},
[address, connectors]
);

function disconnectByClick(): void {
disconnect();
Expand All @@ -107,18 +106,8 @@ const Navbar: FunctionComponent = () => {

function onTopButtonClick(): void {
if (!isConnected) {
refresh();
if (available.length > 0) {
if (available.length === 1) {
connect(available[0]);
} else {
setHasWallet(true);
}
} else {
setHasWallet(true);
}
setHasWallet(true);
} else {
// disconnectByClick();
setShowWallet(true);
}
}
Expand All @@ -129,12 +118,6 @@ const Navbar: FunctionComponent = () => {
return textToReturn;
}

// Refresh available connectors before showing wallet modal
function refreshAndShowWallet(): void {
refresh();
setHasWallet(true);
}

return (
<>
<div className={"fixed w-full z-20 bg-background top-0"}>
Expand Down Expand Up @@ -166,7 +149,7 @@ const Navbar: FunctionComponent = () => {
onClick={
isConnected
? () => setShowWallet(true)
: () => refreshAndShowWallet()
: () => setHasWallet(true)
}
>
{isConnected ? (
Expand Down Expand Up @@ -312,7 +295,6 @@ const Navbar: FunctionComponent = () => {
open={showWallet}
closeModal={() => setShowWallet(false)}
disconnectByClick={disconnectByClick}
hashes={hashes}
setTxLoading={setTxLoading}
/>
<Wallets
Expand Down
8 changes: 4 additions & 4 deletions components/UI/wallets.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo, useState } from "react";
import React from "react";
import styles from "../../styles/components/wallets.module.css";
import { Connector, useAccount, useConnectors } from "@starknet-react/core";
import { Connector, useAccount, useConnect } from "@starknet-react/core";
import Button from "./button";
import { FunctionComponent, useEffect } from "react";
import { Modal } from "@mui/material";
Expand All @@ -17,7 +17,7 @@ const Wallets: FunctionComponent<WalletsProps> = ({
closeWallet,
hasWallet,
}) => {
const { connect, connectors } = useConnectors();
const { connect, connectors } = useConnect();
const { account } = useAccount();
const downloadLinks = useGetDiscoveryWallets(
getDiscoveryWallets.getDiscoveryWallets()
Expand All @@ -30,7 +30,7 @@ const Wallets: FunctionComponent<WalletsProps> = ({
}, [account, closeWallet]);

function connectWallet(connector: Connector): void {
connect(connector);
connect({ connector });
closeWallet();
}

Expand Down
Loading

1 comment on commit fc5158e

@vercel
Copy link

@vercel vercel bot commented on fc5158e Nov 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.