Skip to content

Commit

Permalink
fix: chain order for multiple widget instances
Browse files Browse the repository at this point in the history
  • Loading branch information
chybisov committed Aug 14, 2023
1 parent 0e8dd91 commit 4255ea3
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 69 deletions.
9 changes: 6 additions & 3 deletions packages/widget/src/stores/StoreProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { PropsWithChildren } from 'react';
import type { WidgetConfigProps } from '../types';
import { ChainOrderStoreProvider } from './chains';
import { HeaderStoreProvider } from './header';
import { RouteExecutionStoreProvider } from './routes';
import { SplitSubvariantStoreProvider } from './settings';
Expand All @@ -13,9 +14,11 @@ export const StoreProvider: React.FC<PropsWithChildren<WidgetConfigProps>> = ({
state={config.subvariant === 'split' ? 'swap' : undefined}
>
<HeaderStoreProvider namePrefix={config?.keyPrefix}>
<RouteExecutionStoreProvider namePrefix={config?.keyPrefix}>
{children}
</RouteExecutionStoreProvider>
<ChainOrderStoreProvider namePrefix={config?.keyPrefix}>
<RouteExecutionStoreProvider namePrefix={config?.keyPrefix}>
{children}
</RouteExecutionStoreProvider>
</ChainOrderStoreProvider>
</HeaderStoreProvider>
</SplitSubvariantStoreProvider>
);
Expand Down
49 changes: 49 additions & 0 deletions packages/widget/src/stores/chains/ChainOrderStore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { createContext, useContext, useRef } from 'react';
import type { StoreApi, UseBoundStore } from 'zustand';
import type { PersistStoreProviderProps } from '../types';
import { createChainOrderStore } from './createChainOrderStore';
import type { ChainOrderState } from './types';

export type ChainOrderStore = UseBoundStore<StoreApi<ChainOrderState>>;

export const ChainOrderStoreContext = createContext<ChainOrderStore | null>(
null,
);

export function ChainOrderStoreProvider({
children,
...props
}: PersistStoreProviderProps) {
const storeRef = useRef<ChainOrderStore>();
if (!storeRef.current) {
storeRef.current = createChainOrderStore(props);
}
return (
<ChainOrderStoreContext.Provider value={storeRef.current}>
{children}
</ChainOrderStoreContext.Provider>
);
}

export function useChainOrderStore<T>(
selector: (state: ChainOrderState) => T,
equalityFn?: (left: T, right: T) => boolean,
): T {
const useStore = useContext(ChainOrderStoreContext);
if (!useStore) {
throw new Error(
`You forgot to wrap your component in <${ChainOrderStoreProvider.name}>.`,
);
}
return useStore(selector, equalityFn);
}

export function useChainOrderStoreContext() {
const useStore = useContext(ChainOrderStoreContext);
if (!useStore) {
throw new Error(
`You forgot to wrap your component in <${ChainOrderStoreProvider.name}>.`,
);
}
return useStore;
}
66 changes: 66 additions & 0 deletions packages/widget/src/stores/chains/createChainOrderStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { StateCreator } from 'zustand';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { PersistStoreProps } from '../types';
import type { ChainOrderState } from './types';

export const maxChainToOrder = 9;

export const createChainOrderStore = ({ namePrefix }: PersistStoreProps) =>
create<ChainOrderState>(
persist(
(set, get) => ({
chainOrder: [],
availableChains: [],
initializeChains: (chainIds: number[]) => {
set((state: ChainOrderState) => {
const chainOrder = state.chainOrder.filter((chainId) =>
chainIds.includes(chainId),
);
const chainsToAdd = chainIds.filter(
(chainId) => !chainOrder.includes(chainId),
);
if (chainOrder.length === maxChainToOrder || !chainsToAdd.length) {
return {
availableChains: chainIds,
chainOrder,
};
}
const chainsToAddLength = maxChainToOrder - chainOrder.length;
for (let index = 0; index < chainsToAddLength; index++) {
chainOrder.push(chainsToAdd[index]);
}
return {
availableChains: chainIds,
chainOrder,
};
});
return get().chainOrder;
},
setChain: (chainId: number) => {
const state = get();
if (
state.chainOrder.includes(chainId) ||
!state.availableChains.includes(chainId)
) {
return;
}
set((state: ChainOrderState) => {
const chainOrder = state.chainOrder.slice();
chainOrder.unshift(chainId);
if (chainOrder.length > maxChainToOrder) {
chainOrder.pop();
}
return {
chainOrder,
};
});
},
}),
{
name: `${namePrefix || 'li.fi'}-widget-chains-order`,
version: 0,
partialize: (state) => ({ chainOrder: state.chainOrder }),
},
) as StateCreator<ChainOrderState, [], [], ChainOrderState>,
);
3 changes: 2 additions & 1 deletion packages/widget/src/stores/chains/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './ChainOrderStore';
export * from './createChainOrderStore';
export * from './types';
export * from './useChainOrder';
export * from './useChainOrderStore';
2 changes: 1 addition & 1 deletion packages/widget/src/stores/chains/useChainOrder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { shallow } from 'zustand/shallow';
import { useChainOrderStore } from '.';
import { useChainOrderStore } from './ChainOrderStore';

export const useChainOrder = (): [number[], (chainId: number) => void] => {
return useChainOrderStore(
Expand Down
64 changes: 0 additions & 64 deletions packages/widget/src/stores/chains/useChainOrderStore.ts

This file was deleted.

0 comments on commit 4255ea3

Please sign in to comment.