import { ccc } from "@ckb-ccc/connector"; import React, { CSSProperties, HTMLAttributes, ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react"; import { Connector } from "../components/index.js"; const CCC_CONTEXT = createContext< | { isOpen: boolean; open: () => unknown; close: () => unknown; disconnect: () => unknown; setClient: (client: ccc.Client) => unknown; client: ccc.Client; wallet?: ccc.Wallet; signerInfo?: ccc.SignerInfo; } | undefined >(undefined); class SignersControllerWithFilter extends ccc.SignersController { constructor( public filter?: ( signerInfo: ccc.SignerInfo, wallet: ccc.Wallet, ) => Promise, ) { super(); } async addSigner( walletName: string, icon: string, signerInfo: ccc.SignerInfo, context: ccc.SignersControllerRefreshContext, ) { if ( this.filter && !(await this.filter(signerInfo, { name: walletName, icon })) ) { return; } return super.addSigner(walletName, icon, signerInfo, context); } } export function Provider({ children, connectorProps, hideMark, name, icon, signerFilter, signersController, defaultClient, clientOptions, preferredNetworks, }: { children: ReactNode; connectorProps?: HTMLAttributes<{}>; hideMark?: boolean; name?: string; icon?: string; signerFilter?: ( signerInfo: ccc.SignerInfo, wallet: ccc.Wallet, ) => Promise; signersController?: ccc.SignersController; defaultClient?: ccc.Client; clientOptions?: { icon?: string; client: ccc.Client; name: string }[]; preferredNetworks?: ccc.NetworkPreference[]; }) { const [ref, setRef] = useState(null); const [isOpen, setIsOpen] = useState(false); const [_, setFlag] = useState(0); const defaultSignersController = useRef< SignersControllerWithFilter | undefined >(undefined); const client = useMemo( () => ref?.client ?? new ccc.ClientPublicTestnet(), [ref?.client], ); const open = useCallback(() => { setIsOpen(true); ref?.requestUpdate(); }, [setIsOpen, ref, ref?.requestUpdate]); const close = useCallback(() => { setIsOpen(false); ref?.requestUpdate(); }, [setIsOpen, ref, ref?.requestUpdate]); const disconnect = useMemo( () => ref?.disconnect.bind(ref) ?? (() => {}), [ref, ref?.disconnect], ); const setClient = useMemo( () => ref?.setClient.bind(ref) ?? (() => {}), [ref, ref?.setClient], ); useEffect(() => { if (defaultClient) { setClient(defaultClient); } }, [setClient]); useEffect(() => { if (!defaultSignersController.current) { defaultSignersController.current = new SignersControllerWithFilter( signerFilter, ); } else { defaultSignersController.current.filter = signerFilter; } }, [signerFilter]); return ( setFlag((f) => f + 1)} onClose={close} preferredNetworks={preferredNetworks} clientOptions={clientOptions} {...{ ...connectorProps, style: { zIndex: 999, ...(isOpen ? {} : { display: "none" }), ...({ "--background": "#fff", "--divider": "#eee", "--btn-primary": "#f8f8f8", "--btn-primary-hover": "#efeeee", "--btn-secondary": "#ddd", "--btn-secondary-hover": "#ccc", "--icon-primary": "#1E1E1E", "--icon-secondary": "#666666", color: "#1e1e1e", "--tip-color": "#666", } as CSSProperties), ...connectorProps?.style, }, }} /> {children} ); } export function useCcc() { const context = useContext(CCC_CONTEXT); if (!context) { throw Error( "The component which invokes the useCcc hook should be placed in a ccc.Provider.", ); } return context; }