@dynamic-labs/sdk-react-core
Version:
A React SDK for implementing wallet web3 authentication and authorization to your website.
104 lines (99 loc) • 5.14 kB
JavaScript
'use client'
;
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var dynamicEvents = require('../../events/dynamicEvents.cjs');
var loadingAndLifecycle = require('../../store/state/loadingAndLifecycle/loadingAndLifecycle.cjs');
/** Context for accessing the current user/session's wallets */
const UserWalletsContext = React.createContext(undefined);
const UserWalletsProvider = ({ children }) => {
const [userWallets, _setUserWallets] = React.useState([]);
const prevUserWallets = React.useRef([]);
const addedWalletsIds = React.useRef([]);
const removedWalletsIds = React.useRef([]);
const setUserWallets = React.useCallback((returnUpdatedWallets) => {
_setUserWallets((prevWallets) => {
const updatedWallets = returnUpdatedWallets(prevWallets);
// Use old state value if nothing changed to prevent unnecessary re-renders
if (updatedWallets.length === 0 && prevWallets.length === 0)
return prevWallets;
if (prevWallets.length === 0 && updatedWallets.length > 0) {
dynamicEvents.dynamicEvents.emit('userWalletsPopulated', updatedWallets);
}
prevUserWallets.current = prevWallets;
return updatedWallets;
});
}, []);
// it's an useEffect because it needs to be called after the userWallets state is updated
React.useEffect(() => {
addedWalletsIds.current.forEach((walletId) => {
const wallet = userWallets.find(({ id }) => id === walletId);
if (!wallet)
return;
dynamicEvents.dynamicEvents.emit('walletAdded', wallet, userWallets);
});
removedWalletsIds.current.forEach((walletId) => {
const wallet = prevUserWallets.current.find(({ id }) => id === walletId);
if (!wallet)
return;
dynamicEvents.dynamicEvents.emit('walletRemoved', wallet, userWallets);
});
addedWalletsIds.current = [];
removedWalletsIds.current = [];
}, [userWallets]);
const memoizedRegisterUserWallet = React.useCallback((newWallet) => setUserWallets((userWallets) => [...userWallets, newWallet]), [setUserWallets]);
const memoizedRemoveUserWallet = React.useCallback((walletId) => setUserWallets((userWallets) => userWallets.filter(({ id }) => walletId !== id)), [setUserWallets]);
return (jsxRuntime.jsx(UserWalletsContext.Provider, { value: {
addedWalletsIds,
registerUserWallet: memoizedRegisterUserWallet,
removeUserWallet: memoizedRemoveUserWallet,
removedWalletsIds,
setUserWallets,
userWallets,
}, children: children }));
};
/** Provides access to state and setters of the current user/session wallets array */
const useInternalUserWallets = () => {
const context = React.useContext(UserWalletsContext);
if (!context)
throw new Error('Can only call useInternalUserWallets inside UserWalletsProvider');
return context;
};
/** Provides access to the current user/session wallets */
const useUserWallets = () => {
var _a;
const context = React.useContext(UserWalletsContext);
// EXPLANATION:
//
// The problem:
// When we call useConnectAndSign, the wallet that is then added remains in an incomplete state until
// all tasks initiated by useConnectAndSign have completely finished running.
// If, say, a customer calls switchNetwork() on it in this meantime, the SDK will enter a broken state.
// Can be checked by attempting signMessage with wagmi after entering this broken state.
//
// The Solution:
// In order to prevent customers from accessing these wallets while they are incomplete,
// we return the old value of userWallets until useConnectAndSign has completely finished running.
//
// This is done by setting this "initialWalletVerificationInProgress" flag to true while it runs,
// and storing the previous value of userWallets in verifiedUserWallets.
// We then return this ref instead of the current userWallets.
//
// This ref is only updated when initialWalletVerificationInProgress is false, i.e. when
// there is no wallet being added at the moment.
//
// FYI this same logic is used in useDynamicContext for primaryWallet
const verifiedUserWallets = React.useRef([]);
const { initialWalletVerificationInProgress } = loadingAndLifecycle.useLoadingAndLifecycle();
if (!context)
throw new Error('Can only call useUserWallets inside UserWalletsProvider');
if (!initialWalletVerificationInProgress) {
verifiedUserWallets.current = (_a = context === null || context === void 0 ? void 0 : context.userWallets) !== null && _a !== void 0 ? _a : [];
}
return verifiedUserWallets.current;
};
exports.UserWalletsContext = UserWalletsContext;
exports.UserWalletsProvider = UserWalletsProvider;
exports.useInternalUserWallets = useInternalUserWallets;
exports.useUserWallets = useUserWallets;