Skip to content

Commit

Permalink
Merge pull request #308 from omnisat/feat/state-selectors
Browse files Browse the repository at this point in the history
New Feature: Add option to pass a state selector for less re-renders
  • Loading branch information
ufe-pr authored Jan 30, 2025
2 parents b59f728 + c918a92 commit c9e7b89
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 148 deletions.
5 changes: 4 additions & 1 deletion apps/demo.lasereyes.build/hooks/useUtxos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ const UtxoContext = createContext<UtxoContextType | undefined>(undefined)
export const UtxoProvider: React.FC<{
children: React.ReactNode
}> = ({ children }) => {
const { paymentAddress, network } = useLaserEyes()
const { paymentAddress, network } = useLaserEyes((x) => ({
paymentAddress: x.paymentAddress,
network: x.network,
}))
const mempoolUrl = `${getMempoolSpaceUrl(network)}/api/address/${paymentAddress}/utxo`
const [utxos, setUtxos] = useState<IMempoolUtxo[]>([])

Expand Down
9 changes: 4 additions & 5 deletions packages/lasereyes-core/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export class LaserEyesClient {
},
readonly config?: Config
) {
console.log('LaserEyesClient constructor')
this.$store = stores.$store
this.$network = stores.$network
this.$providerMap = {
Expand All @@ -75,7 +74,6 @@ export class LaserEyesClient {

listenKeys(this.$store, ['isInitializing'], (v, oldValue) => {
if (this.disposed) {
console.warn('Client disposed, ignoring isInitializing change')
return
}

Expand Down Expand Up @@ -122,7 +120,6 @@ export class LaserEyesClient {

async connect(defaultWallet: ProviderType) {
if (this.disposed) {
console.warn('Client disposed, ignoring connect')
return
}

Expand Down Expand Up @@ -186,10 +183,12 @@ export class LaserEyesClient {
localStorage?.removeItem(LOCAL_STORAGE_DEFAULT_WALLET)
}

switchNetwork(network: NetworkType) {
async switchNetwork(network: NetworkType): Promise<void> {
try {
if (this.$store.get().provider) {
this.$providerMap[this.$store.get().provider!]?.switchNetwork(network)
await this.$providerMap[this.$store.get().provider!]?.switchNetwork(
network
)
}
} catch (error) {
if (error instanceof Error) {
Expand Down
2 changes: 1 addition & 1 deletion packages/lasereyes-core/src/client/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export abstract class WalletProvider {
return [this.$store.get().address, this.$store.get().paymentAddress]
}

switchNetwork(_network: NetworkType): void {
async switchNetwork(_network: NetworkType): Promise<void> {
this.parent.disconnect()
throw UNSUPPORTED_PROVIDER_METHOD_ERROR
}
Expand Down
2 changes: 1 addition & 1 deletion packages/lasereyes-core/src/client/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function triggerDOMShakeHack(callback: VoidFunction) {
const node = document.createTextNode(' ')
document.body.appendChild(node)
node.remove()
callback()
Promise.resolve().then(callback)
}, 1500)
}
}
Expand Down
97 changes: 46 additions & 51 deletions packages/lasereyes-react/lib/providers/context.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,55 @@
import {
ContentType,
MAINNET,
createStores,
LaserEyesClient,
LaserEyesStoreType,
NetworkType,
ProviderType,
} from '@omnisat/lasereyes-core'
import { createContext } from 'react'
import { LaserEyesContextType } from './types'
import { MapStore, WritableAtom } from 'nanostores'

export const initialContext = {
hasUnisat: false,
hasXverse: false,
hasOyl: false,
hasMagicEden: false,
hasOkx: false,
hasOrange: false,
hasOpNet: false,
hasLeather: false,
hasPhantom: false,
hasSparrow: false,
hasWizz: false,
isInitializing: true,
connected: false,
isConnecting: false,
publicKey: '',
address: '',
paymentAddress: '',
paymentPublicKey: '',
balance: undefined,
network: MAINNET as NetworkType,
library: null,
provider: null,
accounts: [],
connect: async (_network: ProviderType) => { },
disconnect: () => { },
requestAccounts: async () => [],
getNetwork: async () => MAINNET,
switchNetwork: async (_network: NetworkType) => { },
getPublicKey: async () => '',
const { $store, $network } = createStores()
export const defaultMethods = {
connect: async () => {},
disconnect: () => {},
getBalance: async () => '',
getInscriptions: async () => [],
sendBTC: async (_to: string, _amount: number) => '',
signMessage: async (_message: string) => '',
signPsbt: async (_tx: string) => {
return {
signedPsbtHex: '',
signedPsbtBase64: '',
txId: '',
}
},
pushPsbt: async (_tx: string) => {
return ''
},
inscribe: async (_content: any, _: ContentType) => '',
isCreatingCommit: false,
isInscribing: false,
getNetwork: async () => '',
getPublicKey: async () => '',
pushPsbt: async () => '',
signMessage: async () => '',
requestAccounts: async () => [],
sendBTC: async () => '',
signPsbt: async () => ({
signedPsbtBase64: '',
signedPsbtHex: '',
}),
switchNetwork: async () => {},
inscribe: async () => '',
}

export const LaserEyesContext =
createContext<LaserEyesContextType>(initialContext)
export const LaserEyesStoreContext = createContext<{
$store: MapStore<LaserEyesStoreType>
$network: WritableAtom<NetworkType>
client: LaserEyesClient | null
methods: Pick<
LaserEyesContextType,
| 'switchNetwork'
| 'signPsbt'
| 'signMessage'
| 'requestAccounts'
| 'sendBTC'
| 'inscribe'
| 'getPublicKey'
| 'getNetwork'
| 'pushPsbt'
| 'getInscriptions'
| 'getBalance'
| 'disconnect'
| 'connect'
>
}>({
$store,
$network,
client: null,
methods: defaultMethods,
})
61 changes: 57 additions & 4 deletions packages/lasereyes-react/lib/providers/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,60 @@
import { useContext } from 'react'
import { LaserEyesContext } from './context'
import { useContext, useMemo } from 'react'
import { LaserEyesStoreContext } from './context'
import { LaserEyesContextType } from './types'
import { computed, keepMount, onNotify } from 'nanostores'
import { useStore } from '@nanostores/react'
import { compareValues } from '../utils/comparison'

export const useLaserEyes = (): LaserEyesContextType => {
return useContext(LaserEyesContext)
export function useLaserEyes(): LaserEyesContextType
export function useLaserEyes<T>(selector: (x: LaserEyesContextType) => T): T
export function useLaserEyes<T>(
selector?: (x: LaserEyesContextType) => T
): T | LaserEyesContextType {
const { $network, $store, methods } = useContext(LaserEyesStoreContext)

const $computedStore = useMemo(() => {
const computedStore = computed([$store, $network], (store, network) => {
const value = {
paymentAddress: store.paymentAddress,
address: store.address,
publicKey: store.publicKey,
paymentPublicKey: store.paymentPublicKey,
library: {},
network,
accounts: store.accounts,
balance: Number(store.balance),
connected: store.connected,
isConnecting: store.isConnecting,
isInitializing: store.isInitializing,
provider: store.provider,
hasLeather: store.hasProvider.leather ?? false,
hasMagicEden: store.hasProvider['magic-eden'] ?? false,
hasOkx: store.hasProvider.okx ?? false,
hasOyl: store.hasProvider.oyl ?? false,
hasOrange: store.hasProvider.orange ?? false,
hasOpNet: store.hasProvider.op_net ?? false,
hasPhantom: store.hasProvider.phantom ?? false,
hasUnisat: store.hasProvider.unisat ?? false,
hasSparrow: store.hasProvider.sparrow ?? false,
hasWizz: store.hasProvider.wizz ?? false,
hasXverse: store.hasProvider.xverse ?? false,
...methods,
}
if (typeof selector === 'function') {
return selector(value)
}
return value
})

keepMount(computedStore)
onNotify(computedStore, ({ oldValue, abort }) => {
if (compareValues(oldValue, computedStore.value)) {
abort()
}
})

return computedStore
}, [$network, $store, selector, methods])

return useStore($computedStore)
}
Loading

0 comments on commit c9e7b89

Please sign in to comment.