Skip to content

Commit

Permalink
fix(local-storage): disconnect wallet properly (#156)
Browse files Browse the repository at this point in the history
* fix(local-storage): fix wallet auto reconnecting on disconnect

* fix(select-wallet): remove loading state

* fix(eslint): unused rule

* fix(ord-context): openModal, closeModal

* refactor(select-wallet): move to useCallback

* style(ord-context): dot

* fix(ord-context): don't override initial value
  • Loading branch information
Nanosync authored Nov 20, 2023
1 parent c49c08c commit 8651a05
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 79 deletions.
1 change: 1 addition & 0 deletions packages/ord-connect/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
"import/prefer-default-export": "off",
"@typescript-eslint/no-explicit-any": "warn",
"jsx-a11y/control-has-associated-label": "warn",
"@next/next/no-img-element": "off",
},
overrides: [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useCallback, useState } from "react";

import ChevronRightIcon from "../../assets/chevron-right.svg";
import LoadingIcon from "../../assets/loading.svg";
Expand All @@ -23,22 +23,31 @@ export function WalletButton({
isMobileDevice,
}: WalletButtonProp) {
const [loading, setLoading] = useState(false);

const handleWalletConnectClick = useCallback(async () => {
setLoading(true);
const result = await Promise.race([
onConnect()
.then(() => setLoading(false))
.catch(() => setLoading(false)),
new Promise<string>((resolve) => {
setTimeout(() => resolve("timeout"), 5000);
}),
]);
if (result === "timeout") {
setErrorMessage(
"No wallet pop-up? The extension is not responding. Try reloading your browser.",
);
} else {
setLoading(false);
}
}, [onConnect, setErrorMessage]);

return (
<button
type="button"
className="wallet-option-button"
onClick={async () => {
setLoading(true);
const timeout = (resolve) => setTimeout(() => resolve("timeout"), 5000);
const success = await Promise.race([onConnect(), new Promise(timeout)]);
if (success === "timeout") {
setErrorMessage(
"No wallet pop-up? The extension is not responding. Try reloading your browser.",
);
} else {
setLoading(false);
}
}}
onClick={handleWalletConnectClick}
disabled={isDisabled}
>
<div className="option-wrapper">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Fragment, useCallback, useEffect, useState } from "react";
import { Dialog, Transition } from "@headlessui/react";
import {
AddressFormat,
BrowserWalletNotInstalledError,
BrowserWalletRequestCancelledByUserError,
} from "@ordzaar/ordit-sdk";
Expand Down Expand Up @@ -96,8 +95,8 @@ export function SelectWalletModal({
});
updateWallet(Wallet.UNISAT);
updateFormat({
ordinals: unisatWallet.format as AddressFormat,
payments: unisatWallet.format as AddressFormat,
ordinals: unisatWallet.format,
payments: unisatWallet.format,
});

window.unisat.addListener("accountsChanged", () =>
Expand Down Expand Up @@ -137,8 +136,8 @@ export function SelectWalletModal({
});
updateWallet(Wallet.XVERSE);
updateFormat({
ordinals: taproot.format as AddressFormat,
payments: p2sh.format as AddressFormat,
ordinals: taproot.format,
payments: p2sh.format,
});
closeModal();
return true;
Expand Down
51 changes: 51 additions & 0 deletions packages/ord-connect/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Dispatch, SetStateAction, useCallback, useState } from "react";

const KEY_PREFIX = "ord-connect";

// Helper function to get item from localStorage
function getItemFromLocalStorage<T>(_key: string): T | null {
const key = `${KEY_PREFIX}_${_key}`;
try {
return JSON.parse(localStorage.getItem(key)) as T | null;
} catch (error) {
console.error(`Error retrieving ${key} from localStorage`, error);
return null;
}
}

// Helper function to set item to localStorage
function setItemToLocalStorage<T>(_key: string, value: T) {
const key = `${KEY_PREFIX}_${_key}`;
try {
if (value) {
localStorage.setItem(key, JSON.stringify(value));
} else {
localStorage.removeItem(key);
}
} catch (error) {
console.error(`Error saving ${key} to localStorage`, error);
}
}

export function useLocalStorage<T>(
key: string,
initialValue: T,
): [T, Dispatch<SetStateAction<T>>] {
const [state, setInnerState] = useState<T>(() => {
const value = getItemFromLocalStorage<T>(key);
if (!value) {
setItemToLocalStorage(key, initialValue);
}
return value;
});

const setState = useCallback(
(newValue: T) => {
setItemToLocalStorage(key, newValue);
setInnerState(newValue);
},
[key],
);

return [state, setState];
}
98 changes: 37 additions & 61 deletions packages/ord-connect/src/providers/OrdContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
} from "react";
import { AddressFormat } from "@ordzaar/ordit-sdk";

import { useLocalStorage } from "../hooks/useLocalStorage";

export enum Network {
MAINNET = "mainnet",
TESTNET = "testnet",
Expand Down Expand Up @@ -67,38 +69,12 @@ const OrdContext = createContext<OrdContextType>({
disconnectWallet: NOOP,
});

const KEY_PREFIX = "ord-connect";
const ADDRESS = "address";
const WALLET = "wallet";
const PUBLIC_KEY = "publicKey";
const FORMAT = "format";
const NETWORK = "network";

// Helper function to get item from localStorage
function getItemFromLocalStorage<T>(_key: string): T | null {
const key = `${KEY_PREFIX}_${_key}`;
try {
return JSON.parse(localStorage.getItem(key)) as T | null;
} catch (error) {
console.error(`Error retrieving ${key} from localStorage`, error);
return null;
}
}

// Helper function to set item to localStorage
function setItemToLocalStorage<T>(_key: string, value: T) {
const key = `${KEY_PREFIX}_${_key}`;
try {
if (value) {
localStorage.setItem(key, JSON.stringify(value));
} else {
localStorage.removeItem(key);
}
} catch (error) {
console.error(`Error saving ${key} to localStorage`, error);
}
}

export type OrdConnectProviderProps = {
initialNetwork: Network;
};
Expand All @@ -123,80 +99,80 @@ export type OrdConnectProviderProps = {
* }
*
* @param props - Props object.
* @param props.initialNetwork - Initial network state.
* @param props.initialNetwork - Initial network state if network is not set.
* @returns Provider component for OrdConnect.
*/
export function OrdConnectProvider({
children,
initialNetwork,
}: PropsWithChildren<OrdConnectProviderProps>) {
const [address, setAddress] = useState<BiAddressString>(
() => getItemFromLocalStorage(ADDRESS) ?? EMPTY_BIADDRESS_OBJECT,
);
if (!initialNetwork) {
throw new Error("Initial network cannot be empty");
}

const [network, setNetwork] = useState<Network>(
initialNetwork ?? getItemFromLocalStorage(NETWORK) ?? Network.TESTNET,
const [address, setAddress] = useLocalStorage<BiAddressString>(
ADDRESS,
EMPTY_BIADDRESS_OBJECT,
);

const [wallet, setWallet] = useState<Wallet | null>(() =>
getItemFromLocalStorage(WALLET),
const [network, setNetwork] = useLocalStorage<Network>(
NETWORK,
initialNetwork,
);
const [publicKey, setPublicKey] = useState<BiAddressString>(
() => getItemFromLocalStorage(PUBLIC_KEY) ?? EMPTY_BIADDRESS_OBJECT,

const [wallet, setWallet] = useLocalStorage<Wallet | null>(WALLET, null);
const [publicKey, setPublicKey] = useLocalStorage<BiAddressString>(
PUBLIC_KEY,
EMPTY_BIADDRESS_OBJECT,
);

const [format, setFormat] = useState<BiAddressFormat>(
() => getItemFromLocalStorage(FORMAT) ?? EMPTY_BIADDRESS_OBJECT,
const [format, setFormat] = useLocalStorage<BiAddressFormat>(
FORMAT,
EMPTY_BIADDRESS_OBJECT,
);

const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = useCallback(() => setIsModalOpen(true), []);
const closeModal = useCallback(() => setIsModalOpen(false), []);

const disconnectWallet = useCallback(() => {
setAddress(EMPTY_BIADDRESS_OBJECT);
setPublicKey(EMPTY_BIADDRESS_OBJECT);
setFormat(EMPTY_BIADDRESS_OBJECT);
setWallet(null);
}, []);
}, [setAddress, setFormat, setPublicKey, setWallet]);

const context: OrdContextType = useMemo(
() => ({
address,
updateAddress: (newAddress) => {
setItemToLocalStorage(ADDRESS, newAddress);
setAddress(newAddress);
},
updateAddress: setAddress,
publicKey,
updatePublicKey: (newPublicKey) => {
setItemToLocalStorage(PUBLIC_KEY, newPublicKey);
setPublicKey(newPublicKey);
},
updatePublicKey: setPublicKey,
network,
updateNetwork: (newNetwork) => {
setItemToLocalStorage(NETWORK, newNetwork);
setNetwork(newNetwork);
},
updateNetwork: setNetwork,
wallet,
updateWallet: (newWallet) => {
setItemToLocalStorage(WALLET, newWallet);
setWallet(newWallet);
},
updateWallet: setWallet,
isModalOpen,
openModal: () => setIsModalOpen(true),
closeModal: () => setIsModalOpen(false),
openModal,
closeModal,
format,
updateFormat: (newFormat) => {
setItemToLocalStorage(FORMAT, newFormat);
setFormat(newFormat);
},
updateFormat: setFormat,
disconnectWallet,
}),
[
address,
setAddress,
publicKey,
setPublicKey,
network,
setNetwork,
wallet,
setWallet,
isModalOpen,
openModal,
closeModal,
format,
setFormat,
disconnectWallet,
],
);
Expand Down

0 comments on commit 8651a05

Please sign in to comment.